From 592de8bebcba97441a0996f32bbc09102bbadea3 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Thu, 5 Jul 2018 16:00:53 +0200 Subject: [PATCH 01/49] Model, retrieval, mapping and display for fall back language editing --- .../Upgrade/V_8_0_0/FallbackLanguage.cs | 24 +++++++++++++++++++ src/Umbraco.Core/Models/ILanguage.cs | 6 +++++ src/Umbraco.Core/Models/Language.cs | 8 +++++++ .../Persistence/Dtos/LanguageDto.cs | 9 +++++++ .../Implement/LanguageRepository.cs | 21 +++++++++++++--- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../src/views/languages/edit.controller.js | 14 +++++++++++ .../src/views/languages/edit.html | 15 ++++++++++-- .../views/languages/overview.controller.js | 2 ++ .../src/views/languages/overview.html | 1 + .../Umbraco/config/lang/en_us.xml | 1 + .../Models/ContentEditing/Language.cs | 3 +++ 12 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs new file mode 100644 index 0000000000..f0d7c02b82 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs @@ -0,0 +1,24 @@ +using System.Linq; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + /// + /// Adds a new, self-joined field to umbracoLanguages to hold the fall-back language for + /// a given language. + /// + public class FallbackLanguage : MigrationBase + { + public FallbackLanguage(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray(); + + if (columns.Any(x => x.TableName.InvariantEquals(Constants.DatabaseSchema.Tables.Language) && x.ColumnName.InvariantEquals("fallbackLanguageId")) == false) + AddColumn("fallbackLanguageId"); + } + } +} diff --git a/src/Umbraco.Core/Models/ILanguage.cs b/src/Umbraco.Core/Models/ILanguage.cs index 7bf9e9b32c..f02bd33d2b 100644 --- a/src/Umbraco.Core/Models/ILanguage.cs +++ b/src/Umbraco.Core/Models/ILanguage.cs @@ -33,5 +33,11 @@ namespace Umbraco.Core.Models /// If true, a variant node cannot be published unless this language variant is created /// bool Mandatory { get; set; } + + /// + /// Defines the fallback language that can be used in multi-lingual scenarios to provide + /// content if the requested language does not have it published. + /// + ILanguage FallbackLanguage { get; set; } } } diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs index fa1c9dc826..42c305d492 100644 --- a/src/Umbraco.Core/Models/Language.cs +++ b/src/Umbraco.Core/Models/Language.cs @@ -19,6 +19,7 @@ namespace Umbraco.Core.Models private string _cultureName; private bool _isDefaultVariantLanguage; private bool _mandatory; + private ILanguage _fallbackLanguage; public Language(string isoCode) { @@ -32,6 +33,7 @@ namespace Umbraco.Core.Models public readonly PropertyInfo CultureNameSelector = ExpressionHelper.GetPropertyInfo(x => x.CultureName); public readonly PropertyInfo IsDefaultVariantLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.IsDefaultVariantLanguage); public readonly PropertyInfo MandatorySelector = ExpressionHelper.GetPropertyInfo(x => x.Mandatory); + public readonly PropertyInfo FallbackLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.FallbackLanguage); } /// @@ -71,5 +73,11 @@ namespace Umbraco.Core.Models get => _mandatory; set => SetPropertyValueAndDetectChanges(value, ref _mandatory, Ps.Value.MandatorySelector); } + + public ILanguage FallbackLanguage + { + get => _fallbackLanguage; + set => SetPropertyValueAndDetectChanges(value, ref _fallbackLanguage, Ps.Value.FallbackLanguageSelector); + } } } diff --git a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs index 12c9fd0bd4..f69caf6c91 100644 --- a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs @@ -38,5 +38,14 @@ namespace Umbraco.Core.Persistence.Dtos [Column("mandatory")] [Constraint(Default = "0")] public bool Mandatory { get; set; } + + /// + /// Defines the fallback language that can be used in multi-lingual scenarios to provide + /// content if the requested language does not have it published. + /// + [Column("fallbackLanguageId")] + [ForeignKey(typeof(LanguageDto), Column = "id")] + [Index(IndexTypes.NonClustered)] + public int? FallbackLanguageId { get; set; } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index db2e1124a2..9566247f96 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -52,7 +52,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement sql.OrderBy(dto => dto.Id); // get languages - var languages = Database.Fetch(sql).Select(ConvertFromDto).ToList(); + var dtos = Database.Fetch(sql); + var languages = dtos.Select(ConvertFromDto).ToList(); + PopulateFallbackLanguages(dtos, languages); // initialize the code-id map lock (_codeIdMap) @@ -74,7 +76,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); - return Database.Fetch(sql).Select(ConvertFromDto); + var dtos = Database.Fetch(sql); + var languages = dtos.Select(ConvertFromDto).ToList(); + PopulateFallbackLanguages(dtos, languages); + return languages; } #endregion @@ -199,7 +204,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var entity = LanguageFactory.BuildEntity(dto); return entity; } - + + private static void PopulateFallbackLanguages(List dtos, IList languages) + { + foreach (var dto in dtos.Where(x => x.FallbackLanguageId.HasValue)) + { + var language = languages.Single(x => x.Id == dto.Id); + // ReSharper disable once PossibleInvalidOperationException (DTOs with fallback languages have already been filtered in the loop condition) + language.FallbackLanguage = languages.Single(x => x.Id == dto.FallbackLanguageId.Value); + } + } + public ILanguage GetByIsoCode(string isoCode) { TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 6bd78044f8..67028568eb 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -334,6 +334,7 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js index afb5333ded..9b554b1785 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js @@ -56,6 +56,20 @@ }); + $scope.properties = { + fallbackLanguage: { + alias: "fallbackLanguage", + description: "To allow multi-lingual content to fall back to another language if not present in the requested language, select it here.", + label: "Fall back language" + } + }; + + vm.loading = true; + languageResource.getAll().then(function (languages) { + vm.availableLanguages = languages; + vm.loading = false; + }); + if(!$routeParams.create) { vm.loading = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html index 6aaf915960..adc38de05a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html @@ -14,11 +14,11 @@ hide-alias="true"> - + - + @@ -64,6 +64,17 @@ + +
+ +
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js index c81f93c7d6..2f116ef3cb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js @@ -22,12 +22,14 @@ "treeHeaders_languages", "general_mandatory", "general_default", + "languages_fallsbackToLabel" ]; localizationService.localizeMany(labelKeys).then(function (values) { vm.labels.languages = values[0]; vm.labels.mandatory = values[1]; vm.labels.general = values[2]; + vm.labels.fallsbackTo = values[3]; // set page name vm.page.name = vm.labels.languages; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.html b/src/Umbraco.Web.UI.Client/src/views/languages/overview.html index 90764d3f67..f53326a491 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.html @@ -36,6 +36,7 @@ - {{vm.labels.general}} {{vm.labels.mandatory}} + {{vm.labels.fallsbackTo}}: {{language.fallbackLanguage.name}} Default language An Umbraco site can only have one default langugae set. Switching default language may result in default content missing. + Falls back to diff --git a/src/Umbraco.Web/Models/ContentEditing/Language.cs b/src/Umbraco.Web/Models/ContentEditing/Language.cs index f78d2bd28f..309e111e32 100644 --- a/src/Umbraco.Web/Models/ContentEditing/Language.cs +++ b/src/Umbraco.Web/Models/ContentEditing/Language.cs @@ -24,5 +24,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "isMandatory")] public bool Mandatory { get; set; } + + [DataMember(Name = "fallbackLanguage")] + public Language FallbackLanguage { get; set; } } } From 660fe2d7732f32b5c43999666f20141dc0fd1061 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Thu, 5 Jul 2018 19:37:59 +0200 Subject: [PATCH 02/49] Localised fallback language related messages. Filtered out current languge from list of available fall back languages for selection. --- .../src/views/languages/edit.controller.js | 28 +++++++++++-------- .../src/views/languages/edit.html | 2 +- .../Umbraco/config/lang/en_us.xml | 5 +++- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js index 9b554b1785..3a4961ca81 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js @@ -29,7 +29,10 @@ "languages_mandatoryLanguageHelp", "languages_defaultLanguage", "languages_defaultLanguageHelp", - "languages_addLanguage" + "languages_addLanguage", + "languages_noFallbackLanguageOption", + "languages_fallbackLanguageDescription", + "languages_fallbackLanguage" ]; localizationService.localizeMany(labelKeys).then(function (values) { @@ -39,6 +42,15 @@ vm.labels.defaultLanguage = values[3]; vm.labels.defaultLanguageHelp = values[4]; vm.labels.addLanguage = values[5]; + vm.labels.noFallbackLanguageOption = values[6]; + + $scope.properties = { + fallbackLanguage: { + alias: "fallbackLanguage", + description: values[7], + label: values[8] + } + }; if($routeParams.create) { vm.page.name = vm.labels.addLanguage; @@ -56,21 +68,15 @@ }); - $scope.properties = { - fallbackLanguage: { - alias: "fallbackLanguage", - description: "To allow multi-lingual content to fall back to another language if not present in the requested language, select it here.", - label: "Fall back language" - } - }; - vm.loading = true; languageResource.getAll().then(function (languages) { - vm.availableLanguages = languages; + vm.availableLanguages = languages.filter(function (l) { + return $routeParams.id != l.id; + }); vm.loading = false; }); - if(!$routeParams.create) { + if (!$routeParams.create) { vm.loading = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html index adc38de05a..9ebbc5aede 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html @@ -70,7 +70,7 @@ ng-model="vm.language.fallbackLanguage.id" required ng-options="l.id as l.name for l in vm.availableLanguages"> - + diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 555d4edfb4..ea1db802d5 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1657,11 +1657,14 @@ To manage your website, simply open the Umbraco back office and start adding con Add language Mandatory - Properties on this language has to be filled out before the node can be published. + Properties on this language have to be filled out before the node can be published. Default language An Umbraco site can only have one default langugae set. Switching default language may result in default content missing. Falls back to + No fall back language + To allow multi-lingual content to fall back to another language if not present in the requested language, select it here. + Fall back language From 53e96b25f686b01bac86c8a6f86c84b8df69434f Mon Sep 17 00:00:00 2001 From: AndyButland Date: Fri, 6 Jul 2018 15:23:21 +0200 Subject: [PATCH 03/49] Save of fall-back language on create and update of language --- .../Persistence/Factories/LanguageFactory.cs | 7 +++++ .../Implement/LanguageRepository.cs | 2 -- .../src/views/languages/edit.controller.js | 18 ++++++++++-- .../src/views/languages/edit.html | 1 - .../views/languages/overview.controller.js | 2 +- src/Umbraco.Web/Editors/LanguageController.cs | 28 ++++++++++++++----- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs index 7b24411498..c805ae7f5a 100644 --- a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs @@ -18,7 +18,14 @@ namespace Umbraco.Core.Persistence.Factories { var dto = new LanguageDto { CultureName = entity.CultureName, IsoCode = entity.IsoCode, IsDefaultVariantLanguage = entity.IsDefaultVariantLanguage, Mandatory = entity.Mandatory }; if (entity.HasIdentity) + { dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture)); + } + + if (entity.FallbackLanguage != null) + { + dto.FallbackLanguageId = entity.FallbackLanguage.Id; + } return dto; } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 9566247f96..96bb088f2b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -144,14 +144,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement IsolatedCache.ClearAllCache(); } -; var dto = LanguageFactory.BuildDto(entity); var id = Convert.ToInt32(Database.Insert(dto)); entity.Id = id; entity.ResetDirtyProperties(); - } protected override void PersistUpdatedItem(ILanguage entity) diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js index 3a4961ca81..bc5c421b7b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js @@ -52,7 +52,7 @@ } }; - if($routeParams.create) { + if ($routeParams.create) { vm.page.name = vm.labels.addLanguage; languageResource.getCultures().then(function (culturesDictionary) { var cultures = []; @@ -65,7 +65,6 @@ vm.availableCultures = cultures; }); } - }); vm.loading = true; @@ -99,11 +98,26 @@ }); } + function setCultureForFallbackLanguage(lang) { + for (var i = 0; i < vm.availableLanguages.length; i++) { + if (vm.availableLanguages[i].id === lang.id) { + lang.culture = vm.availableLanguages[i].culture; + break; + } + } + } + function save() { if (formHelper.submitForm({ scope: $scope })) { vm.page.saveButtonState = "busy"; + // We need to attach the ISO code to the fall-back language to pass + // server-side validation. + if (vm.language.fallbackLanguage) { + setCultureForFallbackLanguage(vm.language.fallbackLanguage); + } + languageResource.save(vm.language).then(function (lang) { formHelper.resetForm({ scope: $scope }); diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html index 9ebbc5aede..5570b901e2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html @@ -68,7 +68,6 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js index 2f116ef3cb..c8a728d3aa 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js @@ -34,7 +34,7 @@ vm.page.name = vm.labels.languages; }); - languageResource.getAll().then(function(languages) { + languageResource.getAll().then(function (languages) { vm.languages = languages; vm.loading = false; }); diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index 96019da702..418c8401ff 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; @@ -8,8 +7,6 @@ using System.Web.Http; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Persistence; -using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; @@ -136,21 +133,38 @@ namespace Umbraco.Web.Editors } //create it - var newLang = new Umbraco.Core.Models.Language(culture.Name) + var newLang = new Core.Models.Language(culture.Name) { CultureName = culture.DisplayName, IsDefaultVariantLanguage = language.IsDefaultVariantLanguage, - Mandatory = language.Mandatory + Mandatory = language.Mandatory, }; + + AssociateFallbackLanguage(language, newLang); Services.LocalizationService.Save(newLang); return Mapper.Map(newLang); } found.Mandatory = language.Mandatory; found.IsDefaultVariantLanguage = language.IsDefaultVariantLanguage; + AssociateFallbackLanguage(language, found); Services.LocalizationService.Save(found); return Mapper.Map(found); } - + + private static void AssociateFallbackLanguage(Language submittedLanguage, ILanguage languageToCreateOrUpdate) + { + if (submittedLanguage.FallbackLanguage == null) + { + return; + } + + var fallbackLanguageCulture = CultureInfo.GetCultureInfo(submittedLanguage.FallbackLanguage.IsoCode); + languageToCreateOrUpdate.FallbackLanguage = new Core.Models.Language(fallbackLanguageCulture.Name) + { + Id = submittedLanguage.FallbackLanguage.Id, + CultureName = fallbackLanguageCulture.DisplayName + }; + } } } From 8a34ec886470146ffdbd34cd3060991ce277eb22 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Fri, 6 Jul 2018 15:51:13 +0200 Subject: [PATCH 04/49] Prevented creation of circular fall-back language paths. Allowed for update to no fall-back language. --- .../src/views/languages/edit.controller.js | 5 ++++ src/Umbraco.Web/Editors/LanguageController.cs | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js index bc5c421b7b..79972725fc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js @@ -112,6 +112,11 @@ if (formHelper.submitForm({ scope: $scope })) { vm.page.saveButtonState = "busy"; + // Handle selection of no fall-back language (should pass null) + if (!vm.language.fallbackLanguage.id) { + vm.language.fallbackLanguage = null; + } + // We need to attach the ISO code to the fall-back language to pass // server-side validation. if (vm.language.fallbackLanguage) { diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index 418c8401ff..76a40ac329 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -148,6 +148,13 @@ namespace Umbraco.Web.Editors found.Mandatory = language.Mandatory; found.IsDefaultVariantLanguage = language.IsDefaultVariantLanguage; AssociateFallbackLanguage(language, found); + + if (UpdatedFallbackLanguageCreatesCircularPath(found)) + { + ModelState.AddModelError("FallbackLanguage", "The selected fall back language '" + found.FallbackLanguage.CultureName + "' would create a circular path."); + throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + } + Services.LocalizationService.Save(found); return Mapper.Map(found); } @@ -156,6 +163,7 @@ namespace Umbraco.Web.Editors { if (submittedLanguage.FallbackLanguage == null) { + languageToCreateOrUpdate.FallbackLanguage = null; return; } @@ -166,5 +174,27 @@ namespace Umbraco.Web.Editors CultureName = fallbackLanguageCulture.DisplayName }; } + + private bool UpdatedFallbackLanguageCreatesCircularPath(ILanguage language) + { + if (language.FallbackLanguage == null) + { + return false; + } + + var languages = Services.LocalizationService.GetAllLanguages().ToArray(); + var fallbackLanguage = language.FallbackLanguage; + while (fallbackLanguage != null) + { + if (fallbackLanguage.Id == language.Id) + { + return true; + } + + fallbackLanguage = languages.Single(x => x.Id == fallbackLanguage.Id).FallbackLanguage; + } + + return false; + } } } From 4e859cd88bd0e23e72cfbf3b169d6304d8ebf4cd Mon Sep 17 00:00:00 2001 From: AndyButland Date: Fri, 6 Jul 2018 16:01:54 +0200 Subject: [PATCH 05/49] Prevent delete of a language used as a fall-back for another language --- src/Umbraco.Web/Editors/LanguageController.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index 76a40ac329..0640d16129 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -85,13 +85,23 @@ namespace Umbraco.Web.Editors public IHttpActionResult DeleteLanguage(int id) { var language = Services.LocalizationService.GetLanguageById(id); - if (language == null) return NotFound(); + if (language == null) + { + return NotFound(); + } - var totalLangs = Services.LocalizationService.GetAllLanguages().Count(); + var langs = Services.LocalizationService.GetAllLanguages().ToArray(); + var totalLangs = langs.Length; if (language.IsDefaultVariantLanguage || totalLangs == 1) { - var message = $"Language '{language.IsoCode}' is currently set to 'default' or it is the only installed language and can not be deleted."; + var message = $"Language '{language.CultureName}' is currently set to 'default' or it is the only installed language and cannot be deleted."; + throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message)); + } + + if (language.FallbackLanguage != null && langs.Any(x => x.FallbackLanguage?.Id == language.Id)) + { + var message = $"Language '{language.CultureName}' is defined as a fall-back language for one or more other languages, and so cannot be deleted."; throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message)); } From 3c13b7bd70e79338f2f00aba79104c59472b2a3e Mon Sep 17 00:00:00 2001 From: AndyButland Date: Fri, 6 Jul 2018 23:30:57 +0200 Subject: [PATCH 06/49] Added and updated tests for fall back languages --- .../Persistence/Dtos/LanguageDto.cs | 1 + .../Repositories/LanguageRepositoryTest.cs | 39 ++++++++++++++++--- src/Umbraco.Web/Editors/LanguageController.cs | 8 ++-- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs index f69caf6c91..25ca43f918 100644 --- a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs @@ -46,6 +46,7 @@ namespace Umbraco.Core.Persistence.Dtos [Column("fallbackLanguageId")] [ForeignKey(typeof(LanguageDto), Column = "id")] [Index(IndexTypes.NonClustered)] + [NullSetting(NullSetting = NullSettings.Null)] public int? FallbackLanguageId { get; set; } } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index cd1fe47f39..bda899789d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -47,6 +47,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(language.HasIdentity, Is.True); Assert.That(language.CultureName, Is.EqualTo("en-US")); Assert.That(language.IsoCode, Is.EqualTo("en-US")); + Assert.That(language.FallbackLanguage, Is.Null); } } @@ -61,7 +62,8 @@ namespace Umbraco.Tests.Persistence.Repositories var au = CultureInfo.GetCultureInfo("en-AU"); var language = (ILanguage)new Language(au.Name) { - CultureName = au.DisplayName + CultureName = au.DisplayName, + FallbackLanguage = repository.Get(1) }; repository.Save(language); @@ -73,6 +75,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(language.HasIdentity, Is.True); Assert.That(language.CultureName, Is.EqualTo(au.DisplayName)); Assert.That(language.IsoCode, Is.EqualTo(au.Name)); + Assert.That(language.FallbackLanguage.IsoCode, Is.EqualTo("en-US")); } } @@ -182,7 +185,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(provider); // Act - var languageBR = new Language("pt-BR") {CultureName = "pt-BR"}; + var languageBR = new Language("pt-BR") { CultureName = "pt-BR" }; repository.Save(languageBR); // Assert @@ -190,6 +193,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 Assert.IsFalse(languageBR.IsDefaultVariantLanguage); Assert.IsFalse(languageBR.Mandatory); + Assert.IsNull(languageBR.FallbackLanguage); } } @@ -211,6 +215,31 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 Assert.IsTrue(languageBR.IsDefaultVariantLanguage); Assert.IsTrue(languageBR.Mandatory); + Assert.IsNull(languageBR.FallbackLanguage); + } + } + + [Test] + public void Can_Perform_Add_On_LanguageRepository_With_Fallback_Language() + { + // Arrange + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + var repository = CreateRepository(provider); + + // Act + var languageBR = new Language("pt-BR") + { + CultureName = "pt-BR", + FallbackLanguage = repository.Get(1) + }; + repository.Save(languageBR); + + // Assert + Assert.That(languageBR.HasIdentity, Is.True); + Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 + Assert.That(languageBR.FallbackLanguage.IsoCode, Is.EqualTo("en-US")); } } @@ -232,13 +261,11 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.IsTrue(languageBR.Mandatory); // Act - var languageNZ = new Language("en-NZ") { CultureName = "en-NZ", IsDefaultVariantLanguage = true, Mandatory = true }; repository.Save(languageNZ); languageBR = repository.Get(languageBR.Id); // Assert - Assert.IsFalse(languageBR.IsDefaultVariantLanguage); Assert.IsTrue(languageNZ.IsDefaultVariantLanguage); } @@ -257,6 +284,7 @@ namespace Umbraco.Tests.Persistence.Repositories var language = repository.Get(5); language.IsoCode = "pt-BR"; language.CultureName = "pt-BR"; + language.FallbackLanguage = repository.Get(1); repository.Save(language); @@ -266,6 +294,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(languageUpdated, Is.Not.Null); Assert.That(languageUpdated.IsoCode, Is.EqualTo("pt-BR")); Assert.That(languageUpdated.CultureName, Is.EqualTo("pt-BR")); + Assert.That(languageUpdated.FallbackLanguage.IsoCode, Is.EqualTo("en-US")); } } @@ -314,7 +343,7 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - public void CreateTestData() + private void CreateTestData() { var languageDK = new Language("da-DK") { CultureName = "da-DK" }; ServiceContext.LocalizationService.Save(languageDK);//Id 2 diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index 0640d16129..9b73b50300 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -66,12 +66,11 @@ namespace Umbraco.Web.Editors } else if (allLangs.All(x => !x.IsDefaultVariantLanguage)) { - //if no language has the default flag, then the defaul language is the one with the lowest id + //if no language has the default flag, then the default language is the one with the lowest id model.IsDefaultVariantLanguage = allLangs[0].Id == lang.Id; model.Mandatory = allLangs[0].Id == lang.Id; } } - return model; } @@ -159,7 +158,7 @@ namespace Umbraco.Web.Editors found.IsDefaultVariantLanguage = language.IsDefaultVariantLanguage; AssociateFallbackLanguage(language, found); - if (UpdatedFallbackLanguageCreatesCircularPath(found)) + if (DoesUpdatedFallbackLanguageCreateACircularPath(found)) { ModelState.AddModelError("FallbackLanguage", "The selected fall back language '" + found.FallbackLanguage.CultureName + "' would create a circular path."); throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); @@ -185,7 +184,7 @@ namespace Umbraco.Web.Editors }; } - private bool UpdatedFallbackLanguageCreatesCircularPath(ILanguage language) + private bool DoesUpdatedFallbackLanguageCreateACircularPath(ILanguage language) { if (language.FallbackLanguage == null) { @@ -198,6 +197,7 @@ namespace Umbraco.Web.Editors { if (fallbackLanguage.Id == language.Id) { + // We've found the current language in the path of fall back languages, so we have a circular path. return true; } From aadd843c332c9120064503d9ac2104dd75b8b769 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sun, 8 Jul 2018 15:17:38 +0200 Subject: [PATCH 07/49] Implemented published value fall back via language --- .../IPublishedValueFallback.cs | 10 +- .../NoopPublishedValueFallback.cs | 6 +- .../PublishedContent/PublishedContentTests.cs | 6 +- src/Umbraco.Tests/TestHelpers/BaseWebTest.cs | 2 +- .../PublishedValueFallback.cs | 14 +- .../PublishedValueLanguageFallback.cs | 219 ++++++++++++++++++ src/Umbraco.Web/PublishedContentExtensions.cs | 14 +- .../Runtime/WebRuntimeComponent.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 9 files changed, 251 insertions(+), 23 deletions(-) create mode 100644 src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index 8e1dcfd543..f154d9ef27 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -2,6 +2,12 @@ namespace Umbraco.Core.Models.PublishedContent { + public enum PublishedValueFallbackPriority + { + RecursiveTree, + FallbackLanguage + } + /// /// Provides a fallback strategy for getting values. /// @@ -30,8 +36,8 @@ namespace Umbraco.Core.Models.PublishedContent T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue); - object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse); + object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority); - T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse); + T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs index b99b4ad415..75ab9df35a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -21,9 +21,9 @@ public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) => defaultValue; /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse) => defaultValue; + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) => defaultValue; /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse) => defaultValue; + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) => defaultValue; } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 02f58a00a3..a09cf6d4ad 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -34,11 +34,11 @@ namespace Umbraco.Tests.PublishedContent Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); Container.RegisterSingleton(); - Container.RegisterSingleton(); + Container.RegisterSingleton(); var logger = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(logger)) { Id = 1}, + new DataType(new VoidEditor(logger)) { Id = 1 }, new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 }, new DataType(new RichTextPropertyEditor(logger)) { Id = 1002 }, new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 }, @@ -323,7 +323,7 @@ namespace Umbraco.Tests.PublishedContent } [Test] - public void GetPropertyValueRecursiveTest() + public void Get_Property_Value_Recursive() { var doc = GetNode(1174); var rVal = doc.Value("testRecursive", recurse: true); diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index 5eea6bcf72..2f7fe8700b 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.TestHelpers { base.Compose(); - Container.RegisterSingleton(); + Container.RegisterSingleton(); Container.RegisterSingleton(); } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 47e4b3d872..562b8e393b 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -11,45 +11,45 @@ namespace Umbraco.Web.Models.PublishedContent // kinda reproducing what was available in v7 /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + public virtual object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) { // no fallback here return defaultValue; } /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + public virtual T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) { // no fallback here return defaultValue; } /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + public virtual object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) { // no fallback here return defaultValue; } /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + public virtual T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) { // no fallback here return defaultValue; } /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse) + public virtual object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) { // no fallback here if (!recurse) return defaultValue; // is that ok? - return GetValue(content, alias, culture, segment, defaultValue, recurse); + return GetValue(content, alias, culture, segment, defaultValue, true, fallbackPriority); } /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse) + public virtual T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) { // no fallback here if (!recurse) return defaultValue; diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs new file mode 100644 index 0000000000..c404288a0e --- /dev/null +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs @@ -0,0 +1,219 @@ +using LightInject; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Models.PublishedContent +{ + /// + /// Provides a default implementation for that allows + /// for use of fall-back languages + /// + /// + /// Inherits from that implments what was available in v7. + /// + public class PublishedValueLanguageFallback : PublishedValueFallback + { + /// + /// Gets or sets the services context. + /// + [Inject] + public ServiceContext Services { get; set; } + + /// + public override object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + { + object value; + if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, out value)) + { + return value; + } + + return base.GetValue(property, culture, segment, defaultValue); + } + + /// + public override T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + { + T value; + if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, out value)) + { + return value; + } + + return base.GetValue(property, culture, segment, defaultValue); + } + + /// + public override object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + { + object value; + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, out value)) + { + return value; + } + + return base.GetValue(content, alias, culture, segment, defaultValue); + } + + /// + public override T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + { + T value; + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, out value)) + { + return value; + } + + return base.GetValue(content, alias, culture, segment, defaultValue); + } + + /// + public override object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + { + return GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + } + + /// + public override T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + { + if (fallbackPriority == PublishedValueFallbackPriority.RecursiveTree) + { + var result = base.GetValue(content, alias, culture, segment, defaultValue, recurse, PublishedValueFallbackPriority.RecursiveTree); + if (ValueIsNotNullEmptyOrDefault(result, defaultValue)) + { + // We've prioritised recursive tree search and found a value, so can return it. + return result; + } + + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, out result)) + { + return result; + } + + return defaultValue; + } + + if (fallbackPriority == PublishedValueFallbackPriority.FallbackLanguage) + { + T result; + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, out result)) + { + return result; + } + } + + // No language fall back content found, so use base implementation + return base.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + } + + private static bool ValueIsNotNullEmptyOrDefault(T value, T defaultValue) + { + return value != null && + string.IsNullOrEmpty(value.ToString()) == false && + value.Equals(defaultValue) == false; + } + + private bool TryGetValueFromFallbackLanguage(IPublishedProperty property, string culture, string segment, T defaultValue, out T value) + { + if (string.IsNullOrEmpty(culture)) + { + value = defaultValue; + return false; + } + + var localizationService = Services.LocalizationService; + var language = localizationService.GetLanguageByIsoCode(culture); + if (language.FallbackLanguage == null) + { + value = defaultValue; + return false; + } + + var fallbackLanguage = language.FallbackLanguage; + while (fallbackLanguage != null) + { + value = property.Value(fallbackLanguage.IsoCode, segment, defaultValue); + if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) + { + return true; + } + + fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage, localizationService); + } + + value = defaultValue; + return false; + } + + private bool TryGetValueFromFallbackLanguage(IPublishedElement content, string alias, string culture, string segment, T defaultValue, out T value) + { + if (string.IsNullOrEmpty(culture)) + { + value = defaultValue; + return false; + } + + var localizationService = Services.LocalizationService; + var language = localizationService.GetLanguageByIsoCode(culture); + if (language.FallbackLanguage == null) + { + value = defaultValue; + return false; + } + + var fallbackLanguage = language.FallbackLanguage; + while (fallbackLanguage != null) + { + value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue); + if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) + { + return true; + } + + fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage, localizationService); + } + + value = defaultValue; + return false; + } + + private bool TryGetValueFromFallbackLanguage(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, out T value) + { + if (string.IsNullOrEmpty(culture)) + { + value = defaultValue; + return false; + } + + var localizationService = Services.LocalizationService; + var language = localizationService.GetLanguageByIsoCode(culture); + if (language.FallbackLanguage == null) + { + value = defaultValue; + return false; + } + + var fallbackLanguage = language.FallbackLanguage; + while (fallbackLanguage != null) + { + value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, recurse); + if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) + { + return true; + } + + fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage, localizationService); + } + + value = defaultValue; + return false; + } + + private static ILanguage GetNextFallbackLanguage(ILanguage fallbackLanguage, ILocalizationService localizationService) + { + fallbackLanguage = localizationService.GetLanguageById(fallbackLanguage.Id); // Ensures reference to next fall-back language is loaded if it exists + return fallbackLanguage.FallbackLanguage; + } + } +} diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 1adfb55ca9..22a0bc8aca 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -159,8 +159,9 @@ namespace Umbraco.Web /// The property alias. /// The variation language. /// The variation segment. - /// A value indicating whether to recurse. /// The default value. + /// A value indicating whether to recurse. + /// Flag indicating priority order of fallback paths in cases when content does not exist and a fall back method is used. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// Recursively means: walking up the tree from , get the first value that can be found. @@ -169,14 +170,14 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, bool recurse = false) + public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); } #endregion @@ -193,6 +194,7 @@ namespace Umbraco.Web /// The variation segment. /// The default value. /// A value indicating whether to recurse. + /// Flag indicating priority order of fallback paths in cases when content does not exist and a fall back method is used. /// The value of the content's property identified by the alias, converted to the specified type. /// /// Recursively means: walking up the tree from , get the first value that can be found. @@ -201,18 +203,18 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, bool recurse = false) + public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); } // fixme - .Value() refactoring - in progress - public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false) + public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree) { var aliasesA = aliases.Split(','); if (aliasesA.Length == 0) diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index a4e5db0767..501fb6445d 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -199,7 +199,7 @@ namespace Umbraco.Web.Runtime composition.Container.Register(_ => GlobalHost.ConnectionManager.GetHubContext(), new PerContainerLifetime()); // register properties fallback - composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); } internal void Initialize( diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index f266213da0..e0f2554412 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -253,6 +253,7 @@ + From 11871dc4f7d6565022c0174b8ace37d1ef7a0563 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sun, 8 Jul 2018 16:11:26 +0200 Subject: [PATCH 08/49] Changed the services reference in the published value fall back via language implementation to constructor injection --- .../PublishedValueLanguageFallback.cs | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs index c404288a0e..e6902c00db 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs @@ -1,5 +1,4 @@ -using LightInject; -using Umbraco.Core.Models; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; @@ -14,11 +13,12 @@ namespace Umbraco.Web.Models.PublishedContent /// public class PublishedValueLanguageFallback : PublishedValueFallback { - /// - /// Gets or sets the services context. - /// - [Inject] - public ServiceContext Services { get; set; } + private readonly ILocalizationService _localizationService; + + public PublishedValueLanguageFallback(ILocalizationService localizationService) + { + _localizationService = localizationService; + } /// public override object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) @@ -122,8 +122,7 @@ namespace Umbraco.Web.Models.PublishedContent return false; } - var localizationService = Services.LocalizationService; - var language = localizationService.GetLanguageByIsoCode(culture); + var language = _localizationService.GetLanguageByIsoCode(culture); if (language.FallbackLanguage == null) { value = defaultValue; @@ -139,7 +138,7 @@ namespace Umbraco.Web.Models.PublishedContent return true; } - fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage, localizationService); + fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage); } value = defaultValue; @@ -154,8 +153,7 @@ namespace Umbraco.Web.Models.PublishedContent return false; } - var localizationService = Services.LocalizationService; - var language = localizationService.GetLanguageByIsoCode(culture); + var language = _localizationService.GetLanguageByIsoCode(culture); if (language.FallbackLanguage == null) { value = defaultValue; @@ -171,7 +169,7 @@ namespace Umbraco.Web.Models.PublishedContent return true; } - fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage, localizationService); + fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage); } value = defaultValue; @@ -186,8 +184,7 @@ namespace Umbraco.Web.Models.PublishedContent return false; } - var localizationService = Services.LocalizationService; - var language = localizationService.GetLanguageByIsoCode(culture); + var language = _localizationService.GetLanguageByIsoCode(culture); if (language.FallbackLanguage == null) { value = defaultValue; @@ -203,16 +200,18 @@ namespace Umbraco.Web.Models.PublishedContent return true; } - fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage, localizationService); + fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage); } value = defaultValue; return false; } - private static ILanguage GetNextFallbackLanguage(ILanguage fallbackLanguage, ILocalizationService localizationService) + private ILanguage GetNextFallbackLanguage(ILanguage fallbackLanguage) { - fallbackLanguage = localizationService.GetLanguageById(fallbackLanguage.Id); // Ensures reference to next fall-back language is loaded if it exists + // Ensure reference to next fall-back language is loaded if it exists + fallbackLanguage = _localizationService.GetLanguageById(fallbackLanguage.Id); + return fallbackLanguage.FallbackLanguage; } } From f868bc9589e679aa8cc280f469fb1f73de1299b8 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Wed, 11 Jul 2018 09:23:26 +0200 Subject: [PATCH 09/49] Added fall back language database migration to UmbracoPlan --- src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index b7a77b10ce..9db4710241 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -123,7 +123,7 @@ namespace Umbraco.Core.Migrations.Upgrade Chain("{3608CD41-792A-4E9A-A97D-42A5E797EE31}"); // must chain to v8 final state (see at end of file) - Chain("{4CACE351-C6B9-4F0C-A6BA-85A02BBD39E4}"); + Chain("{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}"); // UPGRADE FROM 7, MORE RECENT @@ -221,10 +221,13 @@ namespace Umbraco.Core.Migrations.Upgrade //Chain("{65D6B71C-BDD5-4A2E-8D35-8896325E9151}"); // stephan added that one - need a path to final state Add("{65D6B71C-BDD5-4A2E-8D35-8896325E9151}", "{4CACE351-C6B9-4F0C-A6BA-85A02BBD39E4}"); + // 8.0.0 + Chain("{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}"); + // FINAL STATE - MUST MATCH LAST ONE ABOVE ! // whenever this changes, update all references in this file! - Add(string.Empty, "{4CACE351-C6B9-4F0C-A6BA-85A02BBD39E4}"); + Add(string.Empty, "{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}"); } } } From 50c5b2ed9213425db1d6d9b1741868b0ae03458e Mon Sep 17 00:00:00 2001 From: AndyButland Date: Wed, 11 Jul 2018 22:15:58 +0100 Subject: [PATCH 10/49] Added test for fallback languages --- .../PublishedContentLanuageVariantTests.cs | 130 ++++++++++ .../PublishedContentMoreTests.cs | 239 ++++++------------ .../PublishedContentSnapshotTestBase.cs | 100 ++++++++ .../SolidPublishedSnapshot.cs | 70 ++++- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 + .../PublishedValueLanguageFallback.cs | 4 +- 6 files changed, 376 insertions(+), 169 deletions(-) create mode 100644 src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs create mode 100644 src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs new file mode 100644 index 0000000000..ea77310977 --- /dev/null +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs @@ -0,0 +1,130 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Composing; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; +using Umbraco.Tests.Testing; +using Umbraco.Web; + +namespace Umbraco.Tests.PublishedContent +{ + [TestFixture] + [UmbracoTest(PluginManager = UmbracoTestOptions.PluginManager.PerFixture)] + public class PublishedContentLanuageVariantTests : PublishedContentSnapshotTestBase + { + protected override void Compose() + { + base.Compose(); + + Container.RegisterSingleton(_ => GetServiceContext()); + } + + protected ServiceContext GetServiceContext() + { + var serviceContext = TestObjects.GetServiceContextMock(Container); + MockLocalizationService(serviceContext); + return serviceContext; + } + + private static void MockLocalizationService(ServiceContext serviceContext) + { + // Set up languages. + // Spanish falls back to English and Italian to Spanish (and then to English). + // French has no fall back. + var languages = new List + { + new Language("en-US") { Id = 1, CultureName = "English", IsDefaultVariantLanguage = true }, + new Language("fr") { Id = 2, CultureName = "French" }, + new Language("es") { Id = 3, CultureName = "Spanish" }, + new Language("it") { Id = 4, CultureName = "Italian" }, + new Language("de") { Id = 5, CultureName = "German" } + }; + languages[2].FallbackLanguage = languages[0]; + languages[3].FallbackLanguage = languages[2]; + + var localizationService = Mock.Get(serviceContext.LocalizationService); + localizationService.Setup(x => x.GetAllLanguages()).Returns(languages); + localizationService.Setup(x => x.GetLanguageByIsoCode(It.IsAny())) + .Returns((string c) => languages.SingleOrDefault(y => y.IsoCode == c)); + } + + internal override void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache) + { + var props = new[] + { + factory.CreatePropertyType("prop1", 1), + }; + var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); + + var prop1 = new SolidPublishedPropertyWithLanguageVariants + { + Alias = "welcomeText", + }; + prop1.SetSourceValue("en-US", "Welcome"); + prop1.SetValue("en-US", "Welcome"); + prop1.SetSourceValue("de", "Willkommen"); + prop1.SetValue("de", "Willkommen"); + + cache.Add(new SolidPublishedContent(contentType1) + { + Id = 1, + SortOrder = 0, + Name = "Content 1", + UrlSegment = "content-1", + Path = "/1", + Level = 1, + Url = "/content-1", + ParentId = -1, + ChildIds = new int[] { }, + Properties = new Collection + { + prop1 + } + }); + } + + [Test] + public void Can_Get_Content_For_Populated_Requested_Language() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "en-US"); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Can_Get_Content_For_Populated_Requested_Non_Default_Language() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "de"); + Assert.AreEqual("Willkommen", value); + } + + [Test] + public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_Without_Fallback() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "fr"); + Assert.IsNull(value); + } + + [Test] + public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "es"); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "it"); + Assert.AreEqual("Welcome", value); + } + } +} diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 101c7827c3..22965ac141 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -1,91 +1,94 @@ -using System; +using System.Collections.ObjectModel; using System.Linq; -using System.Collections.ObjectModel; -using System.Web.Routing; -using Moq; using NUnit.Framework; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web; -using Umbraco.Web.PublishedCache; -using Umbraco.Web.Routing; -using Umbraco.Web.Security; -using Umbraco.Core.Composing; -using Current = Umbraco.Core.Composing.Current; -using LightInject; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.Accessors; namespace Umbraco.Tests.PublishedContent { [TestFixture] [UmbracoTest(PluginManager = UmbracoTestOptions.PluginManager.PerFixture)] - public class PublishedContentMoreTests : PublishedContentTestBase + public class PublishedContentMoreTests : PublishedContentSnapshotTestBase { - // read http://stackoverflow.com/questions/7713326/extension-method-that-works-on-ienumerablet-and-iqueryablet - // and http://msmvps.com/blogs/jon_skeet/archive/2010/10/28/overloading-and-generic-constraints.aspx - // and http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx - - public override void SetUp() + internal override void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache) { - base.SetUp(); + var props = new[] + { + factory.CreatePropertyType("prop1", 1), + }; + var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); + var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), props); + var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), props); - var umbracoContext = GetUmbracoContext(); - Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; - } + cache.Add(new SolidPublishedContent(contentType1) + { + Id = 1, + SortOrder = 0, + Name = "Content 1", + UrlSegment = "content-1", + Path = "/1", + Level = 1, + Url = "/content-1", + ParentId = -1, + ChildIds = new int[] { }, + Properties = new Collection + { + new SolidPublishedProperty + { + Alias = "prop1", + SolidHasValue = true, + SolidValue = 1234, + SolidSourceValue = "1234" + } + } + }); - protected override void Compose() - { - base.Compose(); + cache.Add(new SolidPublishedContent(contentType2) + { + Id = 2, + SortOrder = 1, + Name = "Content 2", + UrlSegment = "content-2", + Path = "/2", + Level = 1, + Url = "/content-2", + ParentId = -1, + ChildIds = new int[] { }, + Properties = new Collection + { + new SolidPublishedProperty + { + Alias = "prop1", + SolidHasValue = true, + SolidValue = 1234, + SolidSourceValue = "1234" + } + } + }); - Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); - } - - protected override TypeLoader CreatePluginManager(IServiceFactory f) - { - var pluginManager = base.CreatePluginManager(f); - - // this is so the model factory looks into the test assembly - pluginManager.AssembliesToScan = pluginManager.AssembliesToScan - .Union(new[] { typeof (PublishedContentMoreTests).Assembly }) - .ToList(); - - return pluginManager; - } - - private UmbracoContext GetUmbracoContext() - { - RouteData routeData = null; - - var publishedSnapshot = CreatePublishedSnapshot(); - - var publishedSnapshotService = new Mock(); - publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot); - - var globalSettings = TestObjects.GetGlobalSettings(); - - var httpContext = GetHttpContextFactory("http://umbraco.local/", routeData).HttpContext; - var umbracoContext = new UmbracoContext( - httpContext, - publishedSnapshotService.Object, - new WebSecurity(httpContext, Current.Services.UserService, globalSettings), - TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), - globalSettings, - new TestVariationContextAccessor()); - - return umbracoContext; - } - - public override void TearDown() - { - base.TearDown(); - - Current.Reset(); + cache.Add(new SolidPublishedContent(contentType2Sub) + { + Id = 3, + SortOrder = 2, + Name = "Content 2Sub", + UrlSegment = "content-2sub", + Path = "/3", + Level = 1, + Url = "/content-2sub", + ParentId = -1, + ChildIds = new int[] { }, + Properties = new Collection + { + new SolidPublishedProperty + { + Alias = "prop1", + SolidHasValue = true, + SolidValue = 1234, + SolidSourceValue = "1234" + } + } + }); } [Test] @@ -197,95 +200,5 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(1, result[0].Id); Assert.AreEqual(2, result[1].Id); } - - private static SolidPublishedSnapshot CreatePublishedSnapshot() - { - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - var caches = new SolidPublishedSnapshot(); - var cache = caches.InnerContentCache; - - var props = new[] - { - factory.CreatePropertyType("prop1", 1), - }; - - var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); - var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), props); - var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), props); - - cache.Add(new SolidPublishedContent(contentType1) - { - Id = 1, - SortOrder = 0, - Name = "Content 1", - UrlSegment = "content-1", - Path = "/1", - Level = 1, - Url = "/content-1", - ParentId = -1, - ChildIds = new int[] {}, - Properties = new Collection - { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } - } - }); - - cache.Add(new SolidPublishedContent(contentType2) - { - Id = 2, - SortOrder = 1, - Name = "Content 2", - UrlSegment = "content-2", - Path = "/2", - Level = 1, - Url = "/content-2", - ParentId = -1, - ChildIds = new int[] { }, - Properties = new Collection - { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } - } - }); - - cache.Add(new SolidPublishedContent(contentType2Sub) - { - Id = 3, - SortOrder = 2, - Name = "Content 2Sub", - UrlSegment = "content-2sub", - Path = "/3", - Level = 1, - Url = "/content-2sub", - ParentId = -1, - ChildIds = new int[] { }, - Properties = new Collection - { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } - } - }); - - return caches; - } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs new file mode 100644 index 0000000000..623472a023 --- /dev/null +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs @@ -0,0 +1,100 @@ +using System; +using System.Linq; +using System.Collections.ObjectModel; +using System.Web.Routing; +using Moq; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; +using Umbraco.Core.Composing; +using Current = Umbraco.Core.Composing.Current; +using LightInject; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing.Objects.Accessors; + +namespace Umbraco.Tests.PublishedContent +{ + public abstract class PublishedContentSnapshotTestBase : PublishedContentTestBase + { + // read http://stackoverflow.com/questions/7713326/extension-method-that-works-on-ienumerablet-and-iqueryablet + // and http://msmvps.com/blogs/jon_skeet/archive/2010/10/28/overloading-and-generic-constraints.aspx + // and http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx + + public override void SetUp() + { + base.SetUp(); + + var umbracoContext = GetUmbracoContext(); + Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; + } + + protected override void Compose() + { + base.Compose(); + + Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); + } + + protected override TypeLoader CreatePluginManager(IServiceFactory f) + { + var pluginManager = base.CreatePluginManager(f); + + // this is so the model factory looks into the test assembly + pluginManager.AssembliesToScan = pluginManager.AssembliesToScan + .Union(new[] { typeof (PublishedContentMoreTests).Assembly }) + .ToList(); + + return pluginManager; + } + + private UmbracoContext GetUmbracoContext() + { + RouteData routeData = null; + + var publishedSnapshot = CreatePublishedSnapshot(); + + var publishedSnapshotService = new Mock(); + publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot); + + var globalSettings = TestObjects.GetGlobalSettings(); + + var httpContext = GetHttpContextFactory("http://umbraco.local/", routeData).HttpContext; + var umbracoContext = new UmbracoContext( + httpContext, + publishedSnapshotService.Object, + new WebSecurity(httpContext, Current.Services.UserService, globalSettings), + TestObjects.GetUmbracoSettings(), + Enumerable.Empty(), + globalSettings, + new TestVariationContextAccessor()); + + return umbracoContext; + } + + public override void TearDown() + { + base.TearDown(); + + Current.Reset(); + } + + private SolidPublishedSnapshot CreatePublishedSnapshot() + { + var dataTypeService = new TestObjects.TestDataTypeService( + new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); + + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); + var caches = new SolidPublishedSnapshot(); + var cache = caches.InnerContentCache; + PopulateCache(factory, cache); + return caches; + } + + internal abstract void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache); + } +} diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 4f63533693..33e315ebec 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -257,10 +257,72 @@ namespace Umbraco.Tests.PublishedContent public bool SolidHasValue { get; set; } public object SolidXPathValue { get; set; } - public object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue; - public object GetValue(string culture = null, string segment = null) => SolidValue; - public object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue; - public bool HasValue(string culture = null, string segment = null) => SolidHasValue; + public virtual object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue; + public virtual object GetValue(string culture = null, string segment = null) => SolidValue; + public virtual object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue; + public virtual bool HasValue(string culture = null, string segment = null) => SolidHasValue; + } + + internal class SolidPublishedPropertyWithLanguageVariants : SolidPublishedProperty + { + private readonly IDictionary _solidSourceValues = new Dictionary(); + private readonly IDictionary _solidValues = new Dictionary(); + private readonly IDictionary _solidXPathValues = new Dictionary(); + + public override object GetSourceValue(string culture = null, string segment = null) + { + if (string.IsNullOrEmpty(culture)) + { + return base.GetSourceValue(culture, segment); + } + + return _solidSourceValues.ContainsKey(culture) ? _solidSourceValues[culture] : null; + } + + public override object GetValue(string culture = null, string segment = null) + { + if (string.IsNullOrEmpty(culture)) + { + return base.GetValue(culture, segment); + } + + return _solidValues.ContainsKey(culture) ? _solidValues[culture] : null; + } + + public override object GetXPathValue(string culture = null, string segment = null) + { + if (string.IsNullOrEmpty(culture)) + { + return base.GetXPathValue(culture, segment); + } + + return _solidXPathValues.ContainsKey(culture) ? _solidXPathValues[culture] : null; + } + + public override bool HasValue(string culture = null, string segment = null) + { + if (string.IsNullOrEmpty(culture)) + { + return base.HasValue(culture, segment); + } + + return _solidSourceValues.ContainsKey(culture); + } + + public void SetSourceValue(string culture, object value) + { + _solidSourceValues.Add(culture, value); + } + + public void SetValue(string culture, object value) + { + _solidValues.Add(culture, value); + } + + public void SetXPathValue(string culture, object value) + { + _solidXPathValues.Add(culture, value); + } } [PublishedModel("ContentType2")] diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c86cf5d2d0..592527a4b6 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -122,6 +122,8 @@ + + diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs index e6902c00db..b6dc9f4244 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs @@ -15,9 +15,9 @@ namespace Umbraco.Web.Models.PublishedContent { private readonly ILocalizationService _localizationService; - public PublishedValueLanguageFallback(ILocalizationService localizationService) + public PublishedValueLanguageFallback(ServiceContext services) { - _localizationService = localizationService; + _localizationService = services.LocalizationService; } /// From d1a31ad8f8383ed1a860e33c8657901405826841 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Thu, 12 Jul 2018 08:12:54 +0100 Subject: [PATCH 11/49] Removed index from fallback language column --- src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs index 25ca43f918..f87930269a 100644 --- a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs @@ -45,7 +45,6 @@ namespace Umbraco.Core.Persistence.Dtos /// [Column("fallbackLanguageId")] [ForeignKey(typeof(LanguageDto), Column = "id")] - [Index(IndexTypes.NonClustered)] [NullSetting(NullSetting = NullSettings.Null)] public int? FallbackLanguageId { get; set; } } From 91a0ee2c93d0a136bad6b0ed8caf44368ea2f625 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Thu, 12 Jul 2018 20:52:02 +0100 Subject: [PATCH 12/49] Refactored to object graph reference of fallback language to just use id --- src/Umbraco.Core/Models/ILanguage.cs | 4 +- src/Umbraco.Core/Models/Language.cs | 10 ++-- .../Persistence/Factories/LanguageFactory.cs | 9 +--- .../Implement/LanguageRepository.cs | 15 +----- .../Repositories/LanguageRepositoryTest.cs | 18 +++---- .../PublishedContentLanuageVariantTests.cs | 8 ++-- .../src/views/languages/edit.controller.js | 20 -------- .../src/views/languages/edit.html | 2 +- .../views/languages/overview.controller.js | 10 ++++ .../src/views/languages/overview.html | 2 +- src/Umbraco.Web/Editors/LanguageController.cs | 47 ++++++++----------- .../Models/ContentEditing/Language.cs | 4 +- .../PublishedValueLanguageFallback.cs | 46 +++++++++--------- 13 files changed, 80 insertions(+), 115 deletions(-) diff --git a/src/Umbraco.Core/Models/ILanguage.cs b/src/Umbraco.Core/Models/ILanguage.cs index f02bd33d2b..8d1c092e13 100644 --- a/src/Umbraco.Core/Models/ILanguage.cs +++ b/src/Umbraco.Core/Models/ILanguage.cs @@ -35,9 +35,9 @@ namespace Umbraco.Core.Models bool Mandatory { get; set; } /// - /// Defines the fallback language that can be used in multi-lingual scenarios to provide + /// Defines the id of a fallback language that can be used in multi-lingual scenarios to provide /// content if the requested language does not have it published. /// - ILanguage FallbackLanguage { get; set; } + int? FallbackLanguageId { get; set; } } } diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs index 42c305d492..5fcd5cd50e 100644 --- a/src/Umbraco.Core/Models/Language.cs +++ b/src/Umbraco.Core/Models/Language.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Models private string _cultureName; private bool _isDefaultVariantLanguage; private bool _mandatory; - private ILanguage _fallbackLanguage; + private int? _fallbackLanguageId; public Language(string isoCode) { @@ -33,7 +33,7 @@ namespace Umbraco.Core.Models public readonly PropertyInfo CultureNameSelector = ExpressionHelper.GetPropertyInfo(x => x.CultureName); public readonly PropertyInfo IsDefaultVariantLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.IsDefaultVariantLanguage); public readonly PropertyInfo MandatorySelector = ExpressionHelper.GetPropertyInfo(x => x.Mandatory); - public readonly PropertyInfo FallbackLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.FallbackLanguage); + public readonly PropertyInfo FallbackLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.FallbackLanguageId); } /// @@ -74,10 +74,10 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _mandatory, Ps.Value.MandatorySelector); } - public ILanguage FallbackLanguage + public int? FallbackLanguageId { - get => _fallbackLanguage; - set => SetPropertyValueAndDetectChanges(value, ref _fallbackLanguage, Ps.Value.FallbackLanguageSelector); + get => _fallbackLanguageId; + set => SetPropertyValueAndDetectChanges(value, ref _fallbackLanguageId, Ps.Value.FallbackLanguageSelector); } } } diff --git a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs index c805ae7f5a..7ab36d15d6 100644 --- a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Persistence.Factories { public static ILanguage BuildEntity(LanguageDto dto) { - var lang = new Language(dto.IsoCode) { CultureName = dto.CultureName, Id = dto.Id, IsDefaultVariantLanguage = dto.IsDefaultVariantLanguage, Mandatory = dto.Mandatory }; + var lang = new Language(dto.IsoCode) { CultureName = dto.CultureName, Id = dto.Id, IsDefaultVariantLanguage = dto.IsDefaultVariantLanguage, Mandatory = dto.Mandatory, FallbackLanguageId = dto.FallbackLanguageId }; // reset dirty initial properties (U4-1946) lang.ResetDirtyProperties(false); return lang; @@ -16,17 +16,12 @@ namespace Umbraco.Core.Persistence.Factories public static LanguageDto BuildDto(ILanguage entity) { - var dto = new LanguageDto { CultureName = entity.CultureName, IsoCode = entity.IsoCode, IsDefaultVariantLanguage = entity.IsDefaultVariantLanguage, Mandatory = entity.Mandatory }; + var dto = new LanguageDto { CultureName = entity.CultureName, IsoCode = entity.IsoCode, IsDefaultVariantLanguage = entity.IsDefaultVariantLanguage, Mandatory = entity.Mandatory, FallbackLanguageId = entity.FallbackLanguageId }; if (entity.HasIdentity) { dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture)); } - if (entity.FallbackLanguage != null) - { - dto.FallbackLanguageId = entity.FallbackLanguage.Id; - } - return dto; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 96bb088f2b..af5d28c18e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -54,7 +54,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // get languages var dtos = Database.Fetch(sql); var languages = dtos.Select(ConvertFromDto).ToList(); - PopulateFallbackLanguages(dtos, languages); // initialize the code-id map lock (_codeIdMap) @@ -77,9 +76,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); var dtos = Database.Fetch(sql); - var languages = dtos.Select(ConvertFromDto).ToList(); - PopulateFallbackLanguages(dtos, languages); - return languages; + return dtos.Select(ConvertFromDto).ToList(); } #endregion @@ -203,16 +200,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return entity; } - private static void PopulateFallbackLanguages(List dtos, IList languages) - { - foreach (var dto in dtos.Where(x => x.FallbackLanguageId.HasValue)) - { - var language = languages.Single(x => x.Id == dto.Id); - // ReSharper disable once PossibleInvalidOperationException (DTOs with fallback languages have already been filtered in the loop condition) - language.FallbackLanguage = languages.Single(x => x.Id == dto.FallbackLanguageId.Value); - } - } - public ILanguage GetByIsoCode(string isoCode) { TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index bda899789d..a63bf5e08d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -47,7 +47,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(language.HasIdentity, Is.True); Assert.That(language.CultureName, Is.EqualTo("en-US")); Assert.That(language.IsoCode, Is.EqualTo("en-US")); - Assert.That(language.FallbackLanguage, Is.Null); + Assert.That(language.FallbackLanguageId, Is.Null); } } @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Persistence.Repositories var language = (ILanguage)new Language(au.Name) { CultureName = au.DisplayName, - FallbackLanguage = repository.Get(1) + FallbackLanguageId = 1 }; repository.Save(language); @@ -75,7 +75,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(language.HasIdentity, Is.True); Assert.That(language.CultureName, Is.EqualTo(au.DisplayName)); Assert.That(language.IsoCode, Is.EqualTo(au.Name)); - Assert.That(language.FallbackLanguage.IsoCode, Is.EqualTo("en-US")); + Assert.That(language.FallbackLanguageId, Is.EqualTo(1)); } } @@ -193,7 +193,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 Assert.IsFalse(languageBR.IsDefaultVariantLanguage); Assert.IsFalse(languageBR.Mandatory); - Assert.IsNull(languageBR.FallbackLanguage); + Assert.IsNull(languageBR.FallbackLanguageId); } } @@ -215,7 +215,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 Assert.IsTrue(languageBR.IsDefaultVariantLanguage); Assert.IsTrue(languageBR.Mandatory); - Assert.IsNull(languageBR.FallbackLanguage); + Assert.IsNull(languageBR.FallbackLanguageId); } } @@ -232,14 +232,14 @@ namespace Umbraco.Tests.Persistence.Repositories var languageBR = new Language("pt-BR") { CultureName = "pt-BR", - FallbackLanguage = repository.Get(1) + FallbackLanguageId = 1 }; repository.Save(languageBR); // Assert Assert.That(languageBR.HasIdentity, Is.True); Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 - Assert.That(languageBR.FallbackLanguage.IsoCode, Is.EqualTo("en-US")); + Assert.That(languageBR.FallbackLanguageId, Is.EqualTo(1)); } } @@ -284,7 +284,7 @@ namespace Umbraco.Tests.Persistence.Repositories var language = repository.Get(5); language.IsoCode = "pt-BR"; language.CultureName = "pt-BR"; - language.FallbackLanguage = repository.Get(1); + language.FallbackLanguageId = 1; repository.Save(language); @@ -294,7 +294,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(languageUpdated, Is.Not.Null); Assert.That(languageUpdated.IsoCode, Is.EqualTo("pt-BR")); Assert.That(languageUpdated.CultureName, Is.EqualTo("pt-BR")); - Assert.That(languageUpdated.FallbackLanguage.IsoCode, Is.EqualTo("en-US")); + Assert.That(languageUpdated.FallbackLanguageId, Is.EqualTo(1)); } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs index ea77310977..22eb4bd799 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs @@ -39,15 +39,15 @@ namespace Umbraco.Tests.PublishedContent { new Language("en-US") { Id = 1, CultureName = "English", IsDefaultVariantLanguage = true }, new Language("fr") { Id = 2, CultureName = "French" }, - new Language("es") { Id = 3, CultureName = "Spanish" }, - new Language("it") { Id = 4, CultureName = "Italian" }, + new Language("es") { Id = 3, CultureName = "Spanish", FallbackLanguageId = 1 }, + new Language("it") { Id = 4, CultureName = "Italian", FallbackLanguageId = 3 }, new Language("de") { Id = 5, CultureName = "German" } }; - languages[2].FallbackLanguage = languages[0]; - languages[3].FallbackLanguage = languages[2]; var localizationService = Mock.Get(serviceContext.LocalizationService); localizationService.Setup(x => x.GetAllLanguages()).Returns(languages); + localizationService.Setup(x => x.GetLanguageById(It.IsAny())) + .Returns((int id) => languages.SingleOrDefault(y => y.Id == id)); localizationService.Setup(x => x.GetLanguageByIsoCode(It.IsAny())) .Returns((string c) => languages.SingleOrDefault(y => y.IsoCode == c)); } diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js index 79972725fc..523ef867cf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js @@ -98,31 +98,11 @@ }); } - function setCultureForFallbackLanguage(lang) { - for (var i = 0; i < vm.availableLanguages.length; i++) { - if (vm.availableLanguages[i].id === lang.id) { - lang.culture = vm.availableLanguages[i].culture; - break; - } - } - } - function save() { if (formHelper.submitForm({ scope: $scope })) { vm.page.saveButtonState = "busy"; - // Handle selection of no fall-back language (should pass null) - if (!vm.language.fallbackLanguage.id) { - vm.language.fallbackLanguage = null; - } - - // We need to attach the ISO code to the fall-back language to pass - // server-side validation. - if (vm.language.fallbackLanguage) { - setCultureForFallbackLanguage(vm.language.fallbackLanguage); - } - languageResource.save(vm.language).then(function (lang) { formHelper.resetForm({ scope: $scope }); diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html index 5570b901e2..a2217a6649 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html @@ -67,7 +67,7 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js index c8a728d3aa..a5c446dfb5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js @@ -13,6 +13,16 @@ vm.editLanguage = editLanguage; vm.deleteLanguage = deleteLanguage; + vm.getLanguageById = function(id) { + for (var i = 0; i < vm.languages.length; i++) { + if (vm.languages[i].id === id) { + return vm.languages[i]; + } + } + + return null; + }; + function init() { vm.loading = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.html b/src/Umbraco.Web.UI.Client/src/views/languages/overview.html index f53326a491..3b75fa62bd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.html @@ -36,7 +36,7 @@ - {{vm.labels.general}} {{vm.labels.mandatory}} - {{vm.labels.fallsbackTo}}: {{language.fallbackLanguage.name}} + {{vm.labels.fallsbackTo}}: {{vm.getLanguageById(language.fallbackLanguageId).name}} x.FallbackLanguage?.Id == language.Id)) + if (langs.Any(x => x.FallbackLanguageId.HasValue && x.FallbackLanguageId.Value == language.Id)) { var message = $"Language '{language.CultureName}' is defined as a fall-back language for one or more other languages, and so cannot be deleted."; throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message)); @@ -147,20 +147,21 @@ namespace Umbraco.Web.Editors CultureName = culture.DisplayName, IsDefaultVariantLanguage = language.IsDefaultVariantLanguage, Mandatory = language.Mandatory, + FallbackLanguageId = language.FallbackLanguageId }; - AssociateFallbackLanguage(language, newLang); Services.LocalizationService.Save(newLang); return Mapper.Map(newLang); } found.Mandatory = language.Mandatory; found.IsDefaultVariantLanguage = language.IsDefaultVariantLanguage; - AssociateFallbackLanguage(language, found); + found.FallbackLanguageId = language.FallbackLanguageId; - if (DoesUpdatedFallbackLanguageCreateACircularPath(found)) + string selectedFallbackLanguageCultureName; + if (DoesUpdatedFallbackLanguageCreateACircularPath(found, out selectedFallbackLanguageCultureName)) { - ModelState.AddModelError("FallbackLanguage", "The selected fall back language '" + found.FallbackLanguage.CultureName + "' would create a circular path."); + ModelState.AddModelError("FallbackLanguage", "The selected fall back language '" + selectedFallbackLanguageCultureName + "' would create a circular path."); throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); } @@ -168,43 +169,35 @@ namespace Umbraco.Web.Editors return Mapper.Map(found); } - private static void AssociateFallbackLanguage(Language submittedLanguage, ILanguage languageToCreateOrUpdate) + private bool DoesUpdatedFallbackLanguageCreateACircularPath(ILanguage language, out string selectedFallbackLanguageCultureName) { - if (submittedLanguage.FallbackLanguage == null) - { - languageToCreateOrUpdate.FallbackLanguage = null; - return; - } - - var fallbackLanguageCulture = CultureInfo.GetCultureInfo(submittedLanguage.FallbackLanguage.IsoCode); - languageToCreateOrUpdate.FallbackLanguage = new Core.Models.Language(fallbackLanguageCulture.Name) - { - Id = submittedLanguage.FallbackLanguage.Id, - CultureName = fallbackLanguageCulture.DisplayName - }; - } - - private bool DoesUpdatedFallbackLanguageCreateACircularPath(ILanguage language) - { - if (language.FallbackLanguage == null) + if (language.FallbackLanguageId.HasValue == false) { + selectedFallbackLanguageCultureName = string.Empty; return false; } var languages = Services.LocalizationService.GetAllLanguages().ToArray(); - var fallbackLanguage = language.FallbackLanguage; - while (fallbackLanguage != null) + var fallbackLanguageId = language.FallbackLanguageId; + while (fallbackLanguageId.HasValue) { - if (fallbackLanguage.Id == language.Id) + if (fallbackLanguageId.Value == language.Id) { // We've found the current language in the path of fall back languages, so we have a circular path. + selectedFallbackLanguageCultureName = GetLanguageFromCollectionById(languages, fallbackLanguageId.Value).CultureName; return true; } - fallbackLanguage = languages.Single(x => x.Id == fallbackLanguage.Id).FallbackLanguage; + fallbackLanguageId = GetLanguageFromCollectionById(languages, fallbackLanguageId.Value).FallbackLanguageId; } + selectedFallbackLanguageCultureName = string.Empty; return false; } + + private static ILanguage GetLanguageFromCollectionById(IEnumerable languages, int id) + { + return languages.Single(x => x.Id == id); + } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/Language.cs b/src/Umbraco.Web/Models/ContentEditing/Language.cs index 309e111e32..7693ee836e 100644 --- a/src/Umbraco.Web/Models/ContentEditing/Language.cs +++ b/src/Umbraco.Web/Models/ContentEditing/Language.cs @@ -25,7 +25,7 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "isMandatory")] public bool Mandatory { get; set; } - [DataMember(Name = "fallbackLanguage")] - public Language FallbackLanguage { get; set; } + [DataMember(Name = "fallbackLanguageId")] + public int? FallbackLanguageId { get; set; } } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs index b6dc9f4244..d6e8db83f4 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs @@ -107,13 +107,6 @@ namespace Umbraco.Web.Models.PublishedContent return base.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); } - private static bool ValueIsNotNullEmptyOrDefault(T value, T defaultValue) - { - return value != null && - string.IsNullOrEmpty(value.ToString()) == false && - value.Equals(defaultValue) == false; - } - private bool TryGetValueFromFallbackLanguage(IPublishedProperty property, string culture, string segment, T defaultValue, out T value) { if (string.IsNullOrEmpty(culture)) @@ -123,22 +116,23 @@ namespace Umbraco.Web.Models.PublishedContent } var language = _localizationService.GetLanguageByIsoCode(culture); - if (language.FallbackLanguage == null) + if (language.FallbackLanguageId.HasValue == false) { value = defaultValue; return false; } - var fallbackLanguage = language.FallbackLanguage; - while (fallbackLanguage != null) + var fallbackLanguageId = language.FallbackLanguageId; + while (fallbackLanguageId.HasValue) { + var fallbackLanguage = GetLanguageById(fallbackLanguageId.Value); value = property.Value(fallbackLanguage.IsoCode, segment, defaultValue); if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) { return true; } - fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage); + fallbackLanguageId = fallbackLanguage.FallbackLanguageId; } value = defaultValue; @@ -154,22 +148,23 @@ namespace Umbraco.Web.Models.PublishedContent } var language = _localizationService.GetLanguageByIsoCode(culture); - if (language.FallbackLanguage == null) + if (language.FallbackLanguageId.HasValue == false) { value = defaultValue; return false; } - var fallbackLanguage = language.FallbackLanguage; - while (fallbackLanguage != null) + var fallbackLanguageId = language.FallbackLanguageId; + while (fallbackLanguageId.HasValue) { + var fallbackLanguage = GetLanguageById(fallbackLanguageId.Value); value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue); if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) { return true; } - fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage); + fallbackLanguageId = fallbackLanguage.FallbackLanguageId; } value = defaultValue; @@ -185,34 +180,39 @@ namespace Umbraco.Web.Models.PublishedContent } var language = _localizationService.GetLanguageByIsoCode(culture); - if (language.FallbackLanguage == null) + if (language.FallbackLanguageId.HasValue == false) { value = defaultValue; return false; } - var fallbackLanguage = language.FallbackLanguage; - while (fallbackLanguage != null) + var fallbackLanguageId = language.FallbackLanguageId; + while (fallbackLanguageId.HasValue) { + var fallbackLanguage = GetLanguageById(fallbackLanguageId.Value); value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, recurse); if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) { return true; } - fallbackLanguage = GetNextFallbackLanguage(fallbackLanguage); + fallbackLanguageId = fallbackLanguage.FallbackLanguageId; } value = defaultValue; return false; } - private ILanguage GetNextFallbackLanguage(ILanguage fallbackLanguage) + private ILanguage GetLanguageById(int id) { - // Ensure reference to next fall-back language is loaded if it exists - fallbackLanguage = _localizationService.GetLanguageById(fallbackLanguage.Id); + return _localizationService.GetLanguageById(id); + } - return fallbackLanguage.FallbackLanguage; + private static bool ValueIsNotNullEmptyOrDefault(T value, T defaultValue) + { + return value != null && + string.IsNullOrEmpty(value.ToString()) == false && + value.Equals(defaultValue) == false; } } } From b1ef9c5aa81a20dcf7f1e14901d3556e75254dc5 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Jul 2018 12:27:14 +0200 Subject: [PATCH 13/49] U4-11502 misc fixes --- src/Umbraco.Core/Models/ILanguage.cs | 32 ++++-- src/Umbraco.Core/Models/Language.cs | 23 ++-- .../IPublishedValueFallback.cs | 103 ++++++++++++++++-- .../Persistence/Dtos/LanguageDto.cs | 18 ++- .../Persistence/Factories/LanguageFactory.cs | 4 +- .../Implement/LanguageRepository.cs | 23 ++-- .../Services/Implement/ContentService.cs | 2 +- .../Repositories/LanguageRepositoryTest.cs | 22 ++-- ...> PublishedContentLanguageVariantTests.cs} | 4 +- .../Services/ContentServiceTests.cs | 6 +- .../Services/LocalizationServiceTests.cs | 10 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Web/Editors/ContentController.cs | 2 +- src/Umbraco.Web/Editors/LanguageController.cs | 22 ++-- .../Models/ContentEditing/Language.cs | 5 +- .../ContentItemDisplayVariationResolver.cs | 4 +- 16 files changed, 188 insertions(+), 94 deletions(-) rename src/Umbraco.Tests/PublishedContent/{PublishedContentLanuageVariantTests.cs => PublishedContentLanguageVariantTests.cs} (97%) diff --git a/src/Umbraco.Core/Models/ILanguage.cs b/src/Umbraco.Core/Models/ILanguage.cs index 8d1c092e13..c0d2fed839 100644 --- a/src/Umbraco.Core/Models/ILanguage.cs +++ b/src/Umbraco.Core/Models/ILanguage.cs @@ -4,40 +4,54 @@ using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models { + /// + /// Represents a language. + /// public interface ILanguage : IEntity, IRememberBeingDirty { /// - /// Gets or sets the Iso Code for the Language + /// Gets or sets the ISO code of the language. /// [DataMember] string IsoCode { get; set; } /// - /// Gets or sets the Culture Name for the Language + /// Gets or sets the culture name of the language. /// [DataMember] string CultureName { get; set; } /// - /// Returns a object for the current Language + /// Gets the object for the language. /// [IgnoreDataMember] CultureInfo CultureInfo { get; } /// - /// Defines if this language is the default variant language when language variants are in use + /// Gets or sets a value indicating whether the language is the default language. /// - bool IsDefaultVariantLanguage { get; set; } + [DataMember] + bool IsDefault { get; set; } /// - /// If true, a variant node cannot be published unless this language variant is created + /// Gets or sets a value indicating whether the language is mandatory. /// - bool Mandatory { get; set; } + /// + /// When a language is mandatory, a multi-lingual document cannot be published + /// without that language being published, and unpublishing that language unpublishes + /// the entire document. + /// + [DataMember] + bool IsMandatory { get; set; } /// - /// Defines the id of a fallback language that can be used in multi-lingual scenarios to provide - /// content if the requested language does not have it published. + /// Gets or sets the identifier of a fallback language. /// + /// + /// The fallback language can be used in multi-lingual scenarios, to help + /// define fallback strategies when a value does not exist for a requested language. + /// + [DataMember] int? FallbackLanguageId { get; set; } } } diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs index 5fcd5cd50e..940648c4b9 100644 --- a/src/Umbraco.Core/Models/Language.cs +++ b/src/Umbraco.Core/Models/Language.cs @@ -31,14 +31,12 @@ namespace Umbraco.Core.Models { public readonly PropertyInfo IsoCodeSelector = ExpressionHelper.GetPropertyInfo(x => x.IsoCode); public readonly PropertyInfo CultureNameSelector = ExpressionHelper.GetPropertyInfo(x => x.CultureName); - public readonly PropertyInfo IsDefaultVariantLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.IsDefaultVariantLanguage); - public readonly PropertyInfo MandatorySelector = ExpressionHelper.GetPropertyInfo(x => x.Mandatory); + public readonly PropertyInfo IsDefaultVariantLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.IsDefault); + public readonly PropertyInfo MandatorySelector = ExpressionHelper.GetPropertyInfo(x => x.IsMandatory); public readonly PropertyInfo FallbackLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.FallbackLanguageId); } - /// - /// Gets or sets the Iso Code for the Language - /// + /// [DataMember] public string IsoCode { @@ -46,9 +44,7 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _isoCode, Ps.Value.IsoCodeSelector); } - /// - /// Gets or sets the Culture Name for the Language - /// + /// [DataMember] public string CultureName { @@ -56,24 +52,25 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _cultureName, Ps.Value.CultureNameSelector); } - /// - /// Returns a object for the current Language - /// + /// [IgnoreDataMember] public CultureInfo CultureInfo => CultureInfo.GetCultureInfo(IsoCode); - public bool IsDefaultVariantLanguage + /// + public bool IsDefault { get => _isDefaultVariantLanguage; set => SetPropertyValueAndDetectChanges(value, ref _isDefaultVariantLanguage, Ps.Value.IsDefaultVariantLanguageSelector); } - public bool Mandatory + /// + public bool IsMandatory { get => _mandatory; set => SetPropertyValueAndDetectChanges(value, ref _mandatory, Ps.Value.MandatorySelector); } + /// public int? FallbackLanguageId { get => _fallbackLanguageId; diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index f154d9ef27..afa70fbd47 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -2,6 +2,8 @@ namespace Umbraco.Core.Models.PublishedContent { + // fixme document + // fixme add values? public enum PublishedValueFallbackPriority { RecursiveTree, @@ -16,28 +18,105 @@ namespace Umbraco.Core.Models.PublishedContent // todo - understand caching vs fallback (recurse etc) public interface IPublishedValueFallback { - // note that at property level, property.GetValue() does NOT implement fallback, and one has - // to get property.Value() or property.Value() to trigger fallback - - // this method is called whenever property.Value(culture, segment, defaultValue) is called, and - // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). - + /// + /// Gets a fallback value for a property. + /// + /// The property. + /// The requested culture. + /// The requested segment. + /// An optional default value. + /// A fallback value, or null. + /// + /// This method is called whenever property.Value(culture, segment, defaultValue) is called, and + /// property.HasValue(culture, segment) is false. + /// It can only fallback at property level (no recurse). + /// At property level, property.GetValue() does *not* implement fallback, and one has to + /// get property.Value() or property.Value{T}() to trigger fallback. + /// object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue); - // this method is called whenever property.Value(culture, segment, defaultValue) is called, and - // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). - + /// + /// Gets a fallback value for a property. + /// + /// The type of the value. + /// The property. + /// The requested culture. + /// The requested segment. + /// An optional default value. + /// A fallback value, or null. + /// + /// This method is called whenever property.Value{T}(culture, segment, defaultValue) is called, and + /// property.HasValue(culture, segment) is false. + /// It can only fallback at property level (no recurse). + /// At property level, property.GetValue() does *not* implement fallback, and one has to + /// get property.Value() or property.Value{T}() to trigger fallback. + /// T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue); - // these methods to be called whenever getting the property value for the specified alias, culture and segment, - // either returned no property at all, or a property that does not HasValue for the specified culture and segment. - + /// + /// Gets a fallback value for a published element property. + /// + /// The published element. + /// The property alias. + /// The requested culture. + /// The requested segment. + /// An optional default value. + /// A fallback value, or null. + /// + /// This method is called whenever getting the property value for the specified alias, culture and + /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. + /// It can only fallback at element level (no recurse). + /// object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue); + /// + /// Gets a fallback value for a published element property. + /// + /// The type of the value. + /// The published element. + /// The property alias. + /// The requested culture. + /// The requested segment. + /// An optional default value. + /// A fallback value, or null. + /// + /// This method is called whenever getting the property value for the specified alias, culture and + /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. + /// It can only fallback at element level (no recurse). + /// T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue); + /// + /// Gets a fallback value for a published content property. + /// + /// The published element. + /// The property alias. + /// The requested culture. + /// The requested segment. + /// An optional default value. + /// A fallback value, or null. + /// + /// This method is called whenever getting the property value for the specified alias, culture and + /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. + /// fixme explain & document priority + merge w/recurse? + /// object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority); + /// + /// Gets a fallback value for a published content property. + /// + /// The type of the value. + /// The published element. + /// The property alias. + /// The requested culture. + /// The requested segment. + /// An optional default value. + /// A fallback value, or null. + /// + /// This method is called whenever getting the property value for the specified alias, culture and + /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. + /// fixme explain & document priority + merge w/recurse? + /// T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority); } } diff --git a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs index f87930269a..f389ab78c3 100644 --- a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs @@ -10,38 +10,46 @@ namespace Umbraco.Core.Persistence.Dtos { public const string TableName = Constants.DatabaseSchema.Tables.Language; + /// + /// Gets or sets the identifier of the language. + /// [Column("id")] [PrimaryKeyColumn(IdentitySeed = 2)] public short Id { get; set; } + /// + /// Gets or sets the ISO code of the language. + /// [Column("languageISOCode")] [Index(IndexTypes.UniqueNonClustered)] [NullSetting(NullSetting = NullSettings.Null)] [Length(10)] public string IsoCode { get; set; } + /// + /// Gets or sets the culture name of the language. + /// [Column("languageCultureName")] [NullSetting(NullSetting = NullSettings.Null)] [Length(100)] public string CultureName { get; set; } /// - /// Defines if this language is the default variant language when language variants are in use + /// Gets or sets a value indicating whether the language is the default language. /// [Column("isDefaultVariantLang")] [Constraint(Default = "0")] public bool IsDefaultVariantLanguage { get; set; } /// - /// If true, a variant node cannot be published unless this language variant is created + /// Gets or sets a value indicating whether the language is mandatory. /// [Column("mandatory")] [Constraint(Default = "0")] - public bool Mandatory { get; set; } + public bool IsMandatory { get; set; } /// - /// Defines the fallback language that can be used in multi-lingual scenarios to provide - /// content if the requested language does not have it published. + /// Gets or sets the identifier of a fallback language. /// [Column("fallbackLanguageId")] [ForeignKey(typeof(LanguageDto), Column = "id")] diff --git a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs index 7ab36d15d6..db2927eea3 100644 --- a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Persistence.Factories { public static ILanguage BuildEntity(LanguageDto dto) { - var lang = new Language(dto.IsoCode) { CultureName = dto.CultureName, Id = dto.Id, IsDefaultVariantLanguage = dto.IsDefaultVariantLanguage, Mandatory = dto.Mandatory, FallbackLanguageId = dto.FallbackLanguageId }; + var lang = new Language(dto.IsoCode) { CultureName = dto.CultureName, Id = dto.Id, IsDefault = dto.IsDefaultVariantLanguage, IsMandatory = dto.IsMandatory, FallbackLanguageId = dto.FallbackLanguageId }; // reset dirty initial properties (U4-1946) lang.ResetDirtyProperties(false); return lang; @@ -16,7 +16,7 @@ namespace Umbraco.Core.Persistence.Factories public static LanguageDto BuildDto(ILanguage entity) { - var dto = new LanguageDto { CultureName = entity.CultureName, IsoCode = entity.IsoCode, IsDefaultVariantLanguage = entity.IsDefaultVariantLanguage, Mandatory = entity.Mandatory, FallbackLanguageId = entity.FallbackLanguageId }; + var dto = new LanguageDto { CultureName = entity.CultureName, IsoCode = entity.IsoCode, IsDefaultVariantLanguage = entity.IsDefault, IsMandatory = entity.IsMandatory, FallbackLanguageId = entity.FallbackLanguageId }; if (entity.HasIdentity) { dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture)); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 787cbc1690..4753b131fe 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -53,12 +53,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // get languages var dtos = Database.Fetch(sql); - var languages = dtos.Select(ConvertFromDto).ToList(); + var languages = dtos.Select(ConvertFromDto).ToList(); // fixme - .OrderBy(x => x.Id) is gone? // fix inconsistencies: there has to be a default language, and it has to be mandatory - var defaultLanguage = languages.FirstOrDefault(x => x.IsDefaultVariantLanguage) ?? languages.First(); - defaultLanguage.IsDefaultVariantLanguage = true; - defaultLanguage.Mandatory = true; + var defaultLanguage = languages.FirstOrDefault(x => x.IsDefault) ?? languages.First(); + defaultLanguage.IsDefault = true; + defaultLanguage.IsMandatory = true; // initialize the code-id map lock (_codeIdMap) @@ -122,10 +122,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return list; } - protected override Guid NodeObjectTypeId - { - get { throw new NotImplementedException(); } - } + protected override Guid NodeObjectTypeId => throw new NotImplementedException(); #endregion @@ -138,7 +135,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement ((EntityBase)entity).AddingEntity(); - if (entity.IsDefaultVariantLanguage) + if (entity.IsDefault) { //if this entity is flagged as the default, we need to set all others to false Database.Execute(Sql().Update(u => u.Set(x => x.IsDefaultVariantLanguage, false))); @@ -161,14 +158,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement ((EntityBase)entity).UpdatingEntity(); - if (entity.IsDefaultVariantLanguage) + if (entity.IsDefault) { //if this entity is flagged as the default, we need to set all others to false Database.Execute(Sql().Update(u => u.Set(x => x.IsDefaultVariantLanguage, false))); //We need to clear the whole cache since all languages will be updated IsolatedCache.ClearAllCache(); } - + var dto = LanguageFactory.BuildDto(entity); Database.Update(dto); @@ -183,7 +180,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistDeletedItem(ILanguage entity) { //we need to validate that we can delete this language - if (entity.IsDefaultVariantLanguage) + if (entity.IsDefault) throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode})"); var count = Database.ExecuteScalar(Sql().SelectCount().From()); @@ -268,7 +265,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement foreach (var language in all) { // if one language is default, return - if (language.IsDefaultVariantLanguage) + if (language.IsDefault) return language; // keep track of language with lowest id if (first == null || language.Id < first.Id) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 30e76468a6..b6d3fece19 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -1082,7 +1082,7 @@ namespace Umbraco.Core.Services.Implement var cannotBePublished = publishedCultures.Count == 0; // no published cultures = cannot be published if (!cannotBePublished) { - var mandatoryCultures = _languageRepository.GetMany().Where(x => x.Mandatory).Select(x => x.IsoCode); + var mandatoryCultures = _languageRepository.GetMany().Where(x => x.IsMandatory).Select(x => x.IsoCode); cannotBePublished = mandatoryCultures.Any(x => !publishedCultures.Contains(x, StringComparer.OrdinalIgnoreCase)); // missing mandatory culture = cannot be published } diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index a63bf5e08d..68d1f71ea8 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -191,8 +191,8 @@ namespace Umbraco.Tests.Persistence.Repositories // Assert Assert.That(languageBR.HasIdentity, Is.True); Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 - Assert.IsFalse(languageBR.IsDefaultVariantLanguage); - Assert.IsFalse(languageBR.Mandatory); + Assert.IsFalse(languageBR.IsDefault); + Assert.IsFalse(languageBR.IsMandatory); Assert.IsNull(languageBR.FallbackLanguageId); } } @@ -207,14 +207,14 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(provider); // Act - var languageBR = new Language("pt-BR") { CultureName = "pt-BR", IsDefaultVariantLanguage = true, Mandatory = true }; + var languageBR = new Language("pt-BR") { CultureName = "pt-BR", IsDefault = true, IsMandatory = true }; repository.Save(languageBR); // Assert Assert.That(languageBR.HasIdentity, Is.True); Assert.That(languageBR.Id, Is.EqualTo(6)); //With 5 existing entries the Id should be 6 - Assert.IsTrue(languageBR.IsDefaultVariantLanguage); - Assert.IsTrue(languageBR.Mandatory); + Assert.IsTrue(languageBR.IsDefault); + Assert.IsTrue(languageBR.IsMandatory); Assert.IsNull(languageBR.FallbackLanguageId); } } @@ -252,22 +252,22 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = CreateRepository(provider); - var languageBR = (ILanguage)new Language("pt-BR") { CultureName = "pt-BR", IsDefaultVariantLanguage = true, Mandatory = true }; + var languageBR = (ILanguage)new Language("pt-BR") { CultureName = "pt-BR", IsDefault = true, IsMandatory = true }; repository.Save(languageBR); var languageEN = new Language("en-AU") { CultureName = "en-AU" }; repository.Save(languageEN); - Assert.IsTrue(languageBR.IsDefaultVariantLanguage); - Assert.IsTrue(languageBR.Mandatory); + Assert.IsTrue(languageBR.IsDefault); + Assert.IsTrue(languageBR.IsMandatory); // Act - var languageNZ = new Language("en-NZ") { CultureName = "en-NZ", IsDefaultVariantLanguage = true, Mandatory = true }; + var languageNZ = new Language("en-NZ") { CultureName = "en-NZ", IsDefault = true, IsMandatory = true }; repository.Save(languageNZ); languageBR = repository.Get(languageBR.Id); // Assert - Assert.IsFalse(languageBR.IsDefaultVariantLanguage); - Assert.IsTrue(languageNZ.IsDefaultVariantLanguage); + Assert.IsFalse(languageBR.IsDefault); + Assert.IsTrue(languageNZ.IsDefault); } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs similarity index 97% rename from src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs rename to src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 22eb4bd799..7f9e7ad954 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -14,7 +14,7 @@ namespace Umbraco.Tests.PublishedContent { [TestFixture] [UmbracoTest(PluginManager = UmbracoTestOptions.PluginManager.PerFixture)] - public class PublishedContentLanuageVariantTests : PublishedContentSnapshotTestBase + public class PublishedContentLanguageVariantTests : PublishedContentSnapshotTestBase { protected override void Compose() { @@ -37,7 +37,7 @@ namespace Umbraco.Tests.PublishedContent // French has no fall back. var languages = new List { - new Language("en-US") { Id = 1, CultureName = "English", IsDefaultVariantLanguage = true }, + new Language("en-US") { Id = 1, CultureName = "English", IsDefault = true }, new Language("fr") { Id = 2, CultureName = "French" }, new Language("es") { Id = 3, CultureName = "Spanish", FallbackLanguageId = 1 }, new Language("it") { Id = 4, CultureName = "Italian", FallbackLanguageId = 3 }, diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index d5003674af..cce264fb3d 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2489,7 +2489,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefaultVariantLanguage = true }; + var langUk = new Language("en-UK") { IsDefault = true }; var langFr = new Language("fr-FR"); languageService.Save(langFr); @@ -2524,7 +2524,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefaultVariantLanguage = true }; + var langUk = new Language("en-UK") { IsDefault = true }; var langFr = new Language("fr-FR"); languageService.Save(langFr); @@ -2562,7 +2562,7 @@ namespace Umbraco.Tests.Services var languageService = ServiceContext.LocalizationService; //var langFr = new Language("fr-FR") { IsDefaultVariantLanguage = true }; - var langXx = new Language("pt-PT") { IsDefaultVariantLanguage = true }; + var langXx = new Language("pt-PT") { IsDefault = true }; var langFr = new Language("fr-FR"); var langUk = new Language("en-UK"); var langDe = new Language("de-DE"); diff --git a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs index 033fa08d4a..cc97c98f5d 100644 --- a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs +++ b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs @@ -362,21 +362,21 @@ namespace Umbraco.Tests.Services { var localizationService = ServiceContext.LocalizationService; var language = new Core.Models.Language("en-AU"); - language.IsDefaultVariantLanguage = true; + language.IsDefault = true; localizationService.Save(language); var result = localizationService.GetLanguageById(language.Id); - Assert.IsTrue(result.IsDefaultVariantLanguage); + Assert.IsTrue(result.IsDefault); var language2 = new Core.Models.Language("en-NZ"); - language2.IsDefaultVariantLanguage = true; + language2.IsDefault = true; localizationService.Save(language2); var result2 = localizationService.GetLanguageById(language2.Id); //re-get result = localizationService.GetLanguageById(language.Id); - Assert.IsTrue(result2.IsDefaultVariantLanguage); - Assert.IsFalse(result.IsDefaultVariantLanguage); + Assert.IsTrue(result2.IsDefault); + Assert.IsFalse(result.IsDefault); } [Test] diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 0b984b1167..bf9e9fefde 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -122,7 +122,7 @@ - + diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 650f0d082b..176e1bc461 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -766,7 +766,7 @@ namespace Umbraco.Web.Editors var mandatoryLangs = Mapper.Map, IEnumerable>(allLangs.Values) .Where(x => otherVariantsToValidate.All(v => !v.Culture.InvariantEquals(x.IsoCode))) //don't include variants above .Where(x => !x.IsoCode.InvariantEquals(contentItem.Culture)) //don't include the current variant - .Where(x => x.Mandatory); + .Where(x => x.IsMandatory); foreach (var lang in mandatoryLangs) { //cannot continue publishing since a required language that is not currently being published isn't published diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index 0de3aca634..7b4e1e36fe 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -57,18 +57,18 @@ namespace Umbraco.Web.Editors //if there's only one language, by default it is the default var allLangs = Services.LocalizationService.GetAllLanguages().OrderBy(x => x.Id).ToList(); - if (!lang.IsDefaultVariantLanguage) + if (!lang.IsDefault) { if (allLangs.Count == 1) { - model.IsDefaultVariantLanguage = true; - model.Mandatory = true; + model.IsDefault = true; + model.IsMandatory = true; } - else if (allLangs.All(x => !x.IsDefaultVariantLanguage)) + else if (allLangs.All(x => !x.IsDefault)) { //if no language has the default flag, then the default language is the one with the lowest id - model.IsDefaultVariantLanguage = allLangs[0].Id == lang.Id; - model.Mandatory = allLangs[0].Id == lang.Id; + model.IsDefault = allLangs[0].Id == lang.Id; + model.IsMandatory = allLangs[0].Id == lang.Id; } } @@ -92,7 +92,7 @@ namespace Umbraco.Web.Editors var langs = Services.LocalizationService.GetAllLanguages().ToArray(); var totalLangs = langs.Length; - if (language.IsDefaultVariantLanguage || totalLangs == 1) + if (language.IsDefault || totalLangs == 1) { var message = $"Language '{language.CultureName}' is currently set to 'default' or it is the only installed language and cannot be deleted."; throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message)); @@ -145,8 +145,8 @@ namespace Umbraco.Web.Editors var newLang = new Core.Models.Language(culture.Name) { CultureName = culture.DisplayName, - IsDefaultVariantLanguage = language.IsDefaultVariantLanguage, - Mandatory = language.Mandatory, + IsDefault = language.IsDefault, + IsMandatory = language.IsMandatory, FallbackLanguageId = language.FallbackLanguageId }; @@ -154,8 +154,8 @@ namespace Umbraco.Web.Editors return Mapper.Map(newLang); } - found.Mandatory = language.Mandatory; - found.IsDefaultVariantLanguage = language.IsDefaultVariantLanguage; + found.IsMandatory = language.IsMandatory; + found.IsDefault = language.IsDefault; found.FallbackLanguageId = language.FallbackLanguageId; string selectedFallbackLanguageCultureName; diff --git a/src/Umbraco.Web/Models/ContentEditing/Language.cs b/src/Umbraco.Web/Models/ContentEditing/Language.cs index 7693ee836e..75dd07bf09 100644 --- a/src/Umbraco.Web/Models/ContentEditing/Language.cs +++ b/src/Umbraco.Web/Models/ContentEditing/Language.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Runtime.CompilerServices; using System.Runtime.Serialization; namespace Umbraco.Web.Models.ContentEditing @@ -20,10 +19,10 @@ namespace Umbraco.Web.Models.ContentEditing public string Name { get; set; } [DataMember(Name = "isDefault")] - public bool IsDefaultVariantLanguage { get; set; } + public bool IsDefault { get; set; } [DataMember(Name = "isMandatory")] - public bool Mandatory { get; set; } + public bool IsMandatory { get; set; } [DataMember(Name = "fallbackLanguageId")] public int? FallbackLanguageId { get; set; } diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs index cb6e2938be..21c27649bb 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs @@ -35,7 +35,7 @@ namespace Umbraco.Web.Models.Mapping var variants = langs.Select(x => new ContentVariation { Language = x, - Mandatory = x.Mandatory, + Mandatory = x.IsMandatory, Name = source.GetCultureName(x.IsoCode), Exists = source.IsCultureAvailable(x.IsoCode), // segments ?? PublishedState = (source.PublishedState == PublishedState.Unpublished //if the entire document is unpublished, then flag every variant as unpublished @@ -61,7 +61,7 @@ namespace Umbraco.Web.Models.Mapping } } if (!foundCurrent) - variants.First(x => x.Language.IsDefaultVariantLanguage).IsCurrent = true; + variants.First(x => x.Language.IsDefault).IsCurrent = true; return variants; } From ecc75bc4c9ae9a12b21dcccded61e32cee3f85e8 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 21 Jul 2018 08:24:08 +0200 Subject: [PATCH 14/49] Allowed delete of langage via services even if used as a fall-back for other languages, by setting references to null before deleting --- .../Persistence/Dtos/LanguageDto.cs | 1 + .../Implement/LanguageRepository.cs | 7 ++++-- .../Services/Implement/LocalizationService.cs | 3 +-- .../Repositories/LanguageRepositoryTest.cs | 24 +++++++++++++++++++ .../Services/LocalizationServiceTests.cs | 14 +++++++++++ 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs index f87930269a..25ca43f918 100644 --- a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs @@ -45,6 +45,7 @@ namespace Umbraco.Core.Persistence.Dtos ///
[Column("fallbackLanguageId")] [ForeignKey(typeof(LanguageDto), Column = "id")] + [Index(IndexTypes.NonClustered)] [NullSetting(NullSetting = NullSettings.Null)] public int? FallbackLanguageId { get; set; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index af5d28c18e..271e084969 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -177,7 +177,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistDeletedItem(ILanguage entity) { - //we need to validate that we can delete this language + // We need to validate that we can delete this language if (entity.IsDefaultVariantLanguage) throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode})"); @@ -185,9 +185,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (count == 1) throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode})"); + // We need to remove any references to the language if it's being used as a fall-back from other ones + Database.Execute(Sql().Update(u => u.Set(x => x.FallbackLanguageId, null)).Where(x => x.FallbackLanguageId == entity.Id)); + base.PersistDeletedItem(entity); - //Clear the cache entries that exist by key/iso + // Clear the cache entries that exist by key/iso IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey(entity.IsoCode)); IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey(entity.CultureName)); } diff --git a/src/Umbraco.Core/Services/Implement/LocalizationService.cs b/src/Umbraco.Core/Services/Implement/LocalizationService.cs index 663ecf586c..e63a3cbdbb 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizationService.cs @@ -393,8 +393,7 @@ namespace Umbraco.Core.Services.Implement return; } - //NOTE: There isn't any constraints in the db, so possible references aren't deleted - + // NOTE: Other than the fall-back language, there aren't any other constraints in the db, so possible references aren't deleted _languageRepository.Delete(language); deleteEventArgs.CanCancel = false; diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index a63bf5e08d..ed82d03c6e 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -318,6 +318,30 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Can_Perform_Delete_On_LanguageRepository_With_Language_Used_As_Fallback() + { + // Arrange + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + // Add language to delete as a fall-back language to another one + var repository = CreateRepository(provider); + var languageToFallbackFrom = repository.Get(5); + languageToFallbackFrom.FallbackLanguageId = 1; + repository.Save(languageToFallbackFrom); + + // Act + var languageToDelete = repository.Get(1); + repository.Delete(languageToDelete); + + var exists = repository.Exists(1); + + // Assert + Assert.That(exists, Is.False); + } + } + [Test] public void Can_Perform_Exists_On_LanguageRepository() { diff --git a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs index 033fa08d4a..ff5aa8edc9 100644 --- a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs +++ b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs @@ -192,6 +192,20 @@ namespace Umbraco.Tests.Services Assert.Null(language); } + [Test] + public void Can_Delete_Language_Used_As_Fallback() + { + var danish = ServiceContext.LocalizationService.GetLanguageByIsoCode("da-DK"); + var norwegian = new Language("nb-NO") { CultureName = "Norwegian", FallbackLanguageId = danish.Id }; + ServiceContext.LocalizationService.Save(norwegian, 0); + var languageId = danish.Id; + + ServiceContext.LocalizationService.Delete(danish); + + var language = ServiceContext.LocalizationService.GetLanguageById(languageId); + Assert.Null(language); + } + [Test] public void Can_Create_DictionaryItem_At_Root() { From 695f21eadbe6aee6415f8983998686db2ffd49f8 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 21 Jul 2018 09:41:07 +0200 Subject: [PATCH 15/49] Removed unnecessary loop in looking up value from a fall-back language. Put in a check to abort fall-back if there's a loop in language fall-backs. --- .../IPublishedValueFallback.cs | 14 +- .../NoopPublishedValueFallback.cs | 16 ++- .../PublishedContentLanuageVariantTests.cs | 14 +- .../PublishedValueFallback.cs | 17 +-- .../PublishedValueLanguageFallback.cs | 129 +++++++++--------- src/Umbraco.Web/PublishedContentExtensions.cs | 15 +- .../PublishedContentPropertyExtension.cs | 9 +- src/Umbraco.Web/PublishedElementExtensions.cs | 11 +- 8 files changed, 127 insertions(+), 98 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index f154d9ef27..d9d9c0c298 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -1,4 +1,4 @@ -using Umbraco.Core.Composing; +using System.Collections.Generic; namespace Umbraco.Core.Models.PublishedContent { @@ -22,22 +22,22 @@ namespace Umbraco.Core.Models.PublishedContent // this method is called whenever property.Value(culture, segment, defaultValue) is called, and // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). - object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue); + object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages); // this method is called whenever property.Value(culture, segment, defaultValue) is called, and // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). - T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue); + T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages); // these methods to be called whenever getting the property value for the specified alias, culture and segment, // either returned no property at all, or a property that does not HasValue for the specified culture and segment. - object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue); + object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages); - T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue); + T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages); - object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority); + object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages); - T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority); + T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs index 75ab9df35a..a7de0709e6 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Models.PublishedContent +using System.Collections.Generic; + +namespace Umbraco.Core.Models.PublishedContent { /// /// Provides a noop implementation for . @@ -9,21 +11,21 @@ public class NoopPublishedValueFallback : IPublishedValueFallback { /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) => defaultValue; + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) => defaultValue; /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) => defaultValue; + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) => defaultValue; /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) => defaultValue; + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) => defaultValue; /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) => defaultValue; + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) => defaultValue; /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) => defaultValue; + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) => defaultValue; /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) => defaultValue; + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) => defaultValue; } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs index 22eb4bd799..17ce032005 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs @@ -35,13 +35,17 @@ namespace Umbraco.Tests.PublishedContent // Set up languages. // Spanish falls back to English and Italian to Spanish (and then to English). // French has no fall back. + // Danish, Swedish and Norweigan create an invalid loop. var languages = new List { new Language("en-US") { Id = 1, CultureName = "English", IsDefaultVariantLanguage = true }, new Language("fr") { Id = 2, CultureName = "French" }, new Language("es") { Id = 3, CultureName = "Spanish", FallbackLanguageId = 1 }, new Language("it") { Id = 4, CultureName = "Italian", FallbackLanguageId = 3 }, - new Language("de") { Id = 5, CultureName = "German" } + new Language("de") { Id = 5, CultureName = "German" }, + new Language("da") { Id = 6, CultureName = "Danish", FallbackLanguageId = 8 }, + new Language("sv") { Id = 7, CultureName = "Swedish", FallbackLanguageId = 6 }, + new Language("no") { Id = 8, CultureName = "Norweigan", FallbackLanguageId = 7 } }; var localizationService = Mock.Get(serviceContext.LocalizationService); @@ -126,5 +130,13 @@ namespace Umbraco.Tests.PublishedContent var value = content.Value("welcomeText", "it"); Assert.AreEqual("Welcome", value); } + + [Test] + public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "no"); + Assert.IsNull(value); + } } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 562b8e393b..86823767fd 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models.PublishedContent; +using System.Collections.Generic; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Models.PublishedContent { @@ -11,45 +12,45 @@ namespace Umbraco.Web.Models.PublishedContent // kinda reproducing what was available in v7 /// - public virtual object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + public virtual object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + public virtual T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + public virtual object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + public virtual T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + public virtual object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) { // no fallback here if (!recurse) return defaultValue; // is that ok? - return GetValue(content, alias, culture, segment, defaultValue, true, fallbackPriority); + return GetValue(content, alias, culture, segment, defaultValue, true, fallbackPriority, visitedLanguages); } /// - public virtual T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + public virtual T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) { // no fallback here if (!recurse) return defaultValue; diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs index d6e8db83f4..d19ef80732 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models; +using System.Collections.Generic; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; @@ -21,72 +22,72 @@ namespace Umbraco.Web.Models.PublishedContent } /// - public override object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + public override object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) { object value; - if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, out value)) + if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, visitedLanguages, out value)) { return value; } - return base.GetValue(property, culture, segment, defaultValue); + return base.GetValue(property, culture, segment, defaultValue, visitedLanguages); } /// - public override T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + public override T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) { T value; - if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, out value)) + if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, visitedLanguages, out value)) { return value; } - return base.GetValue(property, culture, segment, defaultValue); + return base.GetValue(property, culture, segment, defaultValue, visitedLanguages); } /// - public override object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + public override object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) { object value; - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, out value)) + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, visitedLanguages, out value)) { return value; } - return base.GetValue(content, alias, culture, segment, defaultValue); + return base.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages); } /// - public override T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + public override T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) { T value; - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, out value)) + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, visitedLanguages, out value)) { return value; } - return base.GetValue(content, alias, culture, segment, defaultValue); + return base.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages); } /// - public override object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + public override object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) { - return GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + return GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages); } /// - public override T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + public override T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) { if (fallbackPriority == PublishedValueFallbackPriority.RecursiveTree) { - var result = base.GetValue(content, alias, culture, segment, defaultValue, recurse, PublishedValueFallbackPriority.RecursiveTree); + var result = base.GetValue(content, alias, culture, segment, defaultValue, recurse, PublishedValueFallbackPriority.RecursiveTree, visitedLanguages); if (ValueIsNotNullEmptyOrDefault(result, defaultValue)) { // We've prioritised recursive tree search and found a value, so can return it. return result; } - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, out result)) + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages, out result)) { return result; } @@ -97,112 +98,116 @@ namespace Umbraco.Web.Models.PublishedContent if (fallbackPriority == PublishedValueFallbackPriority.FallbackLanguage) { T result; - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, out result)) + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages, out result)) { return result; } } // No language fall back content found, so use base implementation - return base.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + return base.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages); } - private bool TryGetValueFromFallbackLanguage(IPublishedProperty property, string culture, string segment, T defaultValue, out T value) + private bool TryGetValueFromFallbackLanguage(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages, out T value) { + value = defaultValue; + if (string.IsNullOrEmpty(culture)) { - value = defaultValue; return false; } var language = _localizationService.GetLanguageByIsoCode(culture); if (language.FallbackLanguageId.HasValue == false) { - value = defaultValue; return false; } - var fallbackLanguageId = language.FallbackLanguageId; - while (fallbackLanguageId.HasValue) + if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value)) { - var fallbackLanguage = GetLanguageById(fallbackLanguageId.Value); - value = property.Value(fallbackLanguage.IsoCode, segment, defaultValue); - if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) - { - return true; - } - - fallbackLanguageId = fallbackLanguage.FallbackLanguageId; + return false; + } + + visitedLanguages.Add(language.FallbackLanguageId.Value); + + var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); + value = property.Value(fallbackLanguage.IsoCode, segment, defaultValue, visitedLanguages); + if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) + { + return true; } - value = defaultValue; return false; } - private bool TryGetValueFromFallbackLanguage(IPublishedElement content, string alias, string culture, string segment, T defaultValue, out T value) + private bool TryGetValueFromFallbackLanguage(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages, out T value) { + value = defaultValue; + if (string.IsNullOrEmpty(culture)) { - value = defaultValue; return false; } var language = _localizationService.GetLanguageByIsoCode(culture); if (language.FallbackLanguageId.HasValue == false) { - value = defaultValue; return false; } - var fallbackLanguageId = language.FallbackLanguageId; - while (fallbackLanguageId.HasValue) + if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value)) { - var fallbackLanguage = GetLanguageById(fallbackLanguageId.Value); - value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue); - if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) - { - return true; - } - - fallbackLanguageId = fallbackLanguage.FallbackLanguageId; + return false; + } + + visitedLanguages.Add(language.FallbackLanguageId.Value); + + var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); + value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, visitedLanguages); + if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) + { + return true; } - value = defaultValue; return false; } - private bool TryGetValueFromFallbackLanguage(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, out T value) + private bool TryGetValueFromFallbackLanguage(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages, out T value) { + value = defaultValue; if (string.IsNullOrEmpty(culture)) { - value = defaultValue; return false; } var language = _localizationService.GetLanguageByIsoCode(culture); if (language.FallbackLanguageId.HasValue == false) { - value = defaultValue; return false; } - var fallbackLanguageId = language.FallbackLanguageId; - while (fallbackLanguageId.HasValue) + if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value)) { - var fallbackLanguage = GetLanguageById(fallbackLanguageId.Value); - value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, recurse); - if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) - { - return true; - } - - fallbackLanguageId = fallbackLanguage.FallbackLanguageId; + return false; + } + + visitedLanguages.Add(language.FallbackLanguageId.Value); + + var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); + value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, recurse, fallbackPriority, visitedLanguages); + if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) + { + return true; } - value = defaultValue; return false; } + private static bool AlreadyVisitedLanguage(ICollection visitedLanguages, int fallbackLanguageId) + { + return visitedLanguages.Contains(fallbackLanguageId); + } + private ILanguage GetLanguageById(int id) { return _localizationService.GetLanguageById(id); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 22a0bc8aca..4ab2d1dc79 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -162,6 +162,7 @@ namespace Umbraco.Web /// The default value. /// A value indicating whether to recurse. /// Flag indicating priority order of fallback paths in cases when content does not exist and a fall back method is used. + /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// Recursively means: walking up the tree from , get the first value that can be found. @@ -170,14 +171,15 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree) + public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, + bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages ?? new List()); } #endregion @@ -195,6 +197,7 @@ namespace Umbraco.Web /// The default value. /// A value indicating whether to recurse. /// Flag indicating priority order of fallback paths in cases when content does not exist and a fall back method is used. + /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, converted to the specified type. /// /// Recursively means: walking up the tree from , get the first value that can be found. @@ -203,18 +206,20 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree) + public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, + bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages ?? new List()); } // fixme - .Value() refactoring - in progress - public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree) + public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", + bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree, ICollection visitedLanguages = null) { var aliasesA = aliases.Split(','); if (aliasesA.Length == 0) diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs index fcbfc7f431..fdfd772ce7 100644 --- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedContentPropertyExtension.cs @@ -1,4 +1,5 @@ -using Umbraco.Core; +using System.Collections.Generic; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; @@ -15,19 +16,19 @@ namespace Umbraco.Web #region Value - public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default) + public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default, ICollection visitedLanguages = null) { if (property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(property, culture, segment, defaultValue); + return PublishedValueFallback.GetValue(property, culture, segment, defaultValue, visitedLanguages ?? new List()); } #endregion #region Value - public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default) + public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default, ICollection visitedLanguages = null) { // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below) // but we have no way to tell whether default value is specified or not - we could do it with overloads, but then defaultValue diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index 945270cb9e..b0d2826df4 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Web; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; @@ -99,6 +100,7 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. + /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -106,14 +108,14 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default) + public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages ?? new List()); } #endregion @@ -129,6 +131,7 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. + /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, converted to the specified type. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -136,14 +139,14 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default) + public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages ?? new List()); } #endregion From 1be74589f2d3869f504f13a07ad6ce36f9664752 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 21 Jul 2018 15:58:49 +0200 Subject: [PATCH 16/49] Combined fallback parameters into an array that defines methods and priority to be used. --- src/Umbraco.Core/Constants-Content.cs | 22 +++ .../IPublishedValueFallback.cs | 10 +- .../NoopPublishedValueFallback.cs | 4 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../PublishedContentLanuageVariantTests.cs | 102 ++++++++++- .../PublishedContent/PublishedContentTests.cs | 4 +- .../SolidPublishedSnapshot.cs | 22 ++- .../PublishedValueFallback.cs | 78 ++++++--- .../PublishedValueLanguageFallback.cs | 163 ++---------------- src/Umbraco.Web/PublishedContentExtensions.cs | 39 ++--- src/Umbraco.Web/umbraco.presentation/item.cs | 4 +- 11 files changed, 226 insertions(+), 223 deletions(-) create mode 100644 src/Umbraco.Core/Constants-Content.cs diff --git a/src/Umbraco.Core/Constants-Content.cs b/src/Umbraco.Core/Constants-Content.cs new file mode 100644 index 0000000000..4b8c383e6f --- /dev/null +++ b/src/Umbraco.Core/Constants-Content.cs @@ -0,0 +1,22 @@ +namespace Umbraco.Core +{ + public static partial class Constants + { + /// + /// Defines content retrieval related constants + /// + public static class Content + { + /// + /// Defines core supported content fall-back options when retrieving content property values. + /// Defined as constants rather than enum to allow solution or package defined fall-back methods. + /// + public static class FallbackMethods + { + public const int None = 0; + public const int RecursiveTree = 1; + public const int FallbackLanguage = 2; + } + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index d9d9c0c298..7a7b67a2d1 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -2,12 +2,6 @@ namespace Umbraco.Core.Models.PublishedContent { - public enum PublishedValueFallbackPriority - { - RecursiveTree, - FallbackLanguage - } - /// /// Provides a fallback strategy for getting values. /// @@ -36,8 +30,8 @@ namespace Umbraco.Core.Models.PublishedContent T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages); - object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages); + object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages); - T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages); + T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs index a7de0709e6..a8d55176a3 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -23,9 +23,9 @@ namespace Umbraco.Core.Models.PublishedContent public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) => defaultValue; /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) => defaultValue; + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) => defaultValue; /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) => defaultValue; + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) => defaultValue; } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 67028568eb..0a24cc5286 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -286,6 +286,7 @@ + diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs index 17ce032005..7ee2e11209 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs @@ -45,7 +45,8 @@ namespace Umbraco.Tests.PublishedContent new Language("de") { Id = 5, CultureName = "German" }, new Language("da") { Id = 6, CultureName = "Danish", FallbackLanguageId = 8 }, new Language("sv") { Id = 7, CultureName = "Swedish", FallbackLanguageId = 6 }, - new Language("no") { Id = 8, CultureName = "Norweigan", FallbackLanguageId = 7 } + new Language("no") { Id = 8, CultureName = "Norweigan", FallbackLanguageId = 7 }, + new Language("nl") { Id = 9, CultureName = "Dutch", FallbackLanguageId = 1 } }; var localizationService = Mock.Get(serviceContext.LocalizationService); @@ -68,12 +69,28 @@ namespace Umbraco.Tests.PublishedContent { Alias = "welcomeText", }; - prop1.SetSourceValue("en-US", "Welcome"); - prop1.SetValue("en-US", "Welcome"); + prop1.SetSourceValue("en-US", "Welcome", true); + prop1.SetValue("en-US", "Welcome", true); prop1.SetSourceValue("de", "Willkommen"); prop1.SetValue("de", "Willkommen"); + prop1.SetSourceValue("nl", "Welkom"); + prop1.SetValue("nl", "Welkom"); - cache.Add(new SolidPublishedContent(contentType1) + var prop2 = new SolidPublishedPropertyWithLanguageVariants + { + Alias = "welcomeText2", + }; + prop2.SetSourceValue("en-US", "Welcome", true); + prop2.SetValue("en-US", "Welcome", true); + + var prop3 = new SolidPublishedPropertyWithLanguageVariants + { + Alias = "welcomeText", + }; + prop3.SetSourceValue("en-US", "Welcome", true); + prop3.SetValue("en-US", "Welcome", true); + + var item1 = new SolidPublishedContent(contentType1) { Id = 1, SortOrder = 0, @@ -83,12 +100,35 @@ namespace Umbraco.Tests.PublishedContent Level = 1, Url = "/content-1", ParentId = -1, + ChildIds = new[] { 2 }, + Properties = new Collection + { + prop1, prop2 + } + }; + + var item2 = new SolidPublishedContent(contentType1) + { + Id = 2, + SortOrder = 0, + Name = "Content 2", + UrlSegment = "content-2", + Path = "/1/2", + Level = 2, + Url = "/content-1/content-2", + ParentId = 1, ChildIds = new int[] { }, Properties = new Collection { - prop1 + prop3 } - }); + }; + + item1.Children = new List { item2 }; + item2.Parent = item1; + + cache.Add(item1); + cache.Add(item2); } [Test] @@ -116,10 +156,18 @@ namespace Umbraco.Tests.PublishedContent } [Test] - public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() + public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Unless_Requested() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); var value = content.Value("welcomeText", "es"); + Assert.IsNull(value); + } + + [Test] + public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "es", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage }); Assert.AreEqual("Welcome", value); } @@ -127,7 +175,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - var value = content.Value("welcomeText", "it"); + var value = content.Value("welcomeText", "it", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage }); Assert.AreEqual("Welcome", value); } @@ -135,8 +183,44 @@ namespace Umbraco.Tests.PublishedContent public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - var value = content.Value("welcomeText", "no"); + var value = content.Value("welcomeText", "no", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage }); Assert.IsNull(value); } + + [Test] + public void Do_Not_Get_Content_Recursively_Unless_Requested() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText2"); + Assert.IsNull(value); + } + + [Test] + public void Can_Get_Content_Recursively() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText2", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.RecursiveTree }); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Can_Get_Content_With_Recursive_Priority() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText", "nl", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.RecursiveTree, Core.Constants.Content.FallbackMethods.FallbackLanguage }); + + // No Dutch value is directly assigned. Check has fallen back to Dutch value from parent. + Assert.AreEqual("Welkom", value); + } + + [Test] + public void Can_Get_Content_With_Fallback_Language_Priority() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText", "nl", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage, Core.Constants.Content.FallbackMethods.RecursiveTree }); + + // No Dutch value is directly assigned. Check has fallen back to English value from language variant. + Assert.AreEqual("Welcome", value); + } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index a09cf6d4ad..330711a6ba 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -326,8 +326,8 @@ namespace Umbraco.Tests.PublishedContent public void Get_Property_Value_Recursive() { var doc = GetNode(1174); - var rVal = doc.Value("testRecursive", recurse: true); - var nullVal = doc.Value("DoNotFindThis", recurse: true); + var rVal = doc.Value("testRecursive", fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree } ); + var nullVal = doc.Value("DoNotFindThis", fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree }); Assert.AreEqual("This is the recursive val", rVal); Assert.AreEqual(null, nullVal); } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 33e315ebec..cbf7f3189d 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -248,7 +248,7 @@ namespace Umbraco.Tests.PublishedContent #endregion } - class SolidPublishedProperty : IPublishedProperty + internal class SolidPublishedProperty : IPublishedProperty { public PublishedPropertyType PropertyType { get; set; } public string Alias { get; set; } @@ -309,19 +309,33 @@ namespace Umbraco.Tests.PublishedContent return _solidSourceValues.ContainsKey(culture); } - public void SetSourceValue(string culture, object value) + public void SetSourceValue(string culture, object value, bool defaultValue = false) { _solidSourceValues.Add(culture, value); + if (defaultValue) + { + SolidSourceValue = value; + SolidHasValue = true; + } } - public void SetValue(string culture, object value) + public void SetValue(string culture, object value, bool defaultValue = false) { _solidValues.Add(culture, value); + if (defaultValue) + { + SolidValue = value; + SolidHasValue = true; + } } - public void SetXPathValue(string culture, object value) + public void SetXPathValue(string culture, object value, bool defaultValue = false) { _solidXPathValues.Add(culture, value); + if (defaultValue) + { + SolidXPathValue = value; + } } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 86823767fd..85b10be480 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Models.PublishedContent @@ -8,54 +9,79 @@ namespace Umbraco.Web.Models.PublishedContent /// public class PublishedValueFallback : IPublishedValueFallback { - // this is our default implementation // kinda reproducing what was available in v7 /// - public virtual object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) { - // no fallback here - if (!recurse) return defaultValue; - // is that ok? - return GetValue(content, alias, culture, segment, defaultValue, true, fallbackPriority, visitedLanguages); + return GetValue(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages); } /// - public virtual T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) { - // no fallback here - if (!recurse) return defaultValue; + if (fallbackMethods == null) + { + return defaultValue; + } - // otherwise, implement recursion as it was implemented in PublishedContentBase + foreach (var fallbackMethod in fallbackMethods) + { + if (TryGetValueWithFallbackMethod(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages, fallbackMethod, out T value)) + { + return value; + } + } + + return defaultValue; + } + + protected virtual bool TryGetValueWithFallbackMethod(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages, int fallbackMethod, out T value) + { + value = defaultValue; + switch (fallbackMethod) + { + case Core.Constants.Content.FallbackMethods.None: + return false; + case Core.Constants.Content.FallbackMethods.RecursiveTree: + return TryGetValueWithRecursiveTree(content, alias, culture, segment, defaultValue, out value); + default: + throw new NotSupportedException($"Fallback method with indentifying number {fallbackMethod} is not supported within {GetType().Name}."); + } + } + + protected static bool TryGetValueWithRecursiveTree(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value) + { + // Implement recursion as it was implemented in PublishedContentBase // fixme caching? // @@ -73,21 +99,31 @@ namespace Umbraco.Web.Models.PublishedContent { content = content.Parent; property = content?.GetProperty(alias); - if (property != null) noValueProperty = property; - } while (content != null && (property == null || property.HasValue(culture, segment) == false)); + if (property != null) + { + noValueProperty = property; + } + } + while (content != null && (property == null || property.HasValue(culture, segment) == false)); // if we found a content with the property having a value, return that property value if (property != null && property.HasValue(culture, segment)) - return property.Value(culture, segment); + { + value = property.Value(culture, segment); + return true; + } // if we found a property, even though with no value, return that property value // because the converter may want to handle the missing value. ie if defaultValue is default, // either specified or by default, the converter may want to substitute something else. if (noValueProperty != null) - return noValueProperty.Value(culture, segment, defaultValue: defaultValue); + { + value = noValueProperty.Value(culture, segment, defaultValue: defaultValue); + return true; + } - // else return default - return defaultValue; + value = defaultValue; + return false; } } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs index d19ef80732..e94a8559a0 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; @@ -21,158 +23,23 @@ namespace Umbraco.Web.Models.PublishedContent _localizationService = services.LocalizationService; } - /// - public override object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) - { - object value; - if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, visitedLanguages, out value)) - { - return value; - } - - return base.GetValue(property, culture, segment, defaultValue, visitedLanguages); - } - - /// - public override T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) - { - T value; - if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, visitedLanguages, out value)) - { - return value; - } - - return base.GetValue(property, culture, segment, defaultValue, visitedLanguages); - } - - /// - public override object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) - { - object value; - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, visitedLanguages, out value)) - { - return value; - } - - return base.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages); - } - - /// - public override T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) - { - T value; - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, visitedLanguages, out value)) - { - return value; - } - - return base.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages); - } - - /// - public override object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) - { - return GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages); - } - - /// - public override T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) - { - if (fallbackPriority == PublishedValueFallbackPriority.RecursiveTree) - { - var result = base.GetValue(content, alias, culture, segment, defaultValue, recurse, PublishedValueFallbackPriority.RecursiveTree, visitedLanguages); - if (ValueIsNotNullEmptyOrDefault(result, defaultValue)) - { - // We've prioritised recursive tree search and found a value, so can return it. - return result; - } - - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages, out result)) - { - return result; - } - - return defaultValue; - } - - if (fallbackPriority == PublishedValueFallbackPriority.FallbackLanguage) - { - T result; - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages, out result)) - { - return result; - } - } - - // No language fall back content found, so use base implementation - return base.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages); - } - - private bool TryGetValueFromFallbackLanguage(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages, out T value) + protected override bool TryGetValueWithFallbackMethod(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages, int fallbackMethod, out T value) { value = defaultValue; - - if (string.IsNullOrEmpty(culture)) + switch (fallbackMethod) { - return false; + case Core.Constants.Content.FallbackMethods.None: + return false; + case Core.Constants.Content.FallbackMethods.RecursiveTree: + return TryGetValueWithRecursiveTree(content, alias, culture, segment, defaultValue, out value); + case Core.Constants.Content.FallbackMethods.FallbackLanguage: + return TryGetValueWithFallbackLanguage(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages, out value); + default: + throw new NotSupportedException($"Fallback method with indentifying number {fallbackMethod} is not supported within {GetType().Name}."); } - - var language = _localizationService.GetLanguageByIsoCode(culture); - if (language.FallbackLanguageId.HasValue == false) - { - return false; - } - - if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value)) - { - return false; - } - - visitedLanguages.Add(language.FallbackLanguageId.Value); - - var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); - value = property.Value(fallbackLanguage.IsoCode, segment, defaultValue, visitedLanguages); - if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) - { - return true; - } - - return false; } - private bool TryGetValueFromFallbackLanguage(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages, out T value) - { - value = defaultValue; - - if (string.IsNullOrEmpty(culture)) - { - return false; - } - - var language = _localizationService.GetLanguageByIsoCode(culture); - if (language.FallbackLanguageId.HasValue == false) - { - return false; - } - - if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value)) - { - return false; - } - - visitedLanguages.Add(language.FallbackLanguageId.Value); - - var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); - value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, visitedLanguages); - if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) - { - return true; - } - - return false; - } - - private bool TryGetValueFromFallbackLanguage(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages, out T value) + private bool TryGetValueWithFallbackLanguage(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages, out T value) { value = defaultValue; if (string.IsNullOrEmpty(culture)) @@ -194,7 +61,7 @@ namespace Umbraco.Web.Models.PublishedContent visitedLanguages.Add(language.FallbackLanguageId.Value); var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); - value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, recurse, fallbackPriority, visitedLanguages); + value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, fallbackMethods.ToArray(), visitedLanguages); if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) { return true; diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 4ab2d1dc79..c4c745a087 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -151,43 +151,35 @@ namespace Umbraco.Web #endregion #region Value - + /// - /// Recursively the value of a content's property identified by its alias, if it exists, otherwise a default value. + /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value. /// /// The content. /// The property alias. /// The variation language. /// The variation segment. /// The default value. - /// A value indicating whether to recurse. - /// Flag indicating priority order of fallback paths in cases when content does not exist and a fall back method is used. + /// Options for fall-back if content not found. /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. - /// - /// Recursively means: walking up the tree from , get the first value that can be found. - /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. - /// If no property with the specified alias exists, or if the property has no value, returns . - /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. - /// The alias is case-insensitive. - /// - public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, - bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree, ICollection visitedLanguages = null) + public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, + int[] fallbackMethods = null, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages ?? new List()); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages ?? new List()); } #endregion #region Value - + /// - /// Recursively gets the value of a content's property identified by its alias, converted to a specified type. + /// Gets the value of a content's property identified by its alias, converted to a specified type. /// /// The target property type. /// The content. @@ -195,31 +187,24 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. - /// A value indicating whether to recurse. - /// Flag indicating priority order of fallback paths in cases when content does not exist and a fall back method is used. + /// Options for fall-back if content not found. /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, converted to the specified type. /// - /// Recursively means: walking up the tree from , get the first value that can be found. - /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. - /// If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns default(T). - /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. - /// The alias is case-insensitive. - /// public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, - bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree, ICollection visitedLanguages = null) + int[] fallbackMethods = null, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages ?? new List()); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages ?? new List()); } // fixme - .Value() refactoring - in progress public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", - bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree, ICollection visitedLanguages = null) + int[] fallbackMethods = null, ICollection visitedLanguages = null) { var aliasesA = aliases.Split(','); if (aliasesA.Length == 0) diff --git a/src/Umbraco.Web/umbraco.presentation/item.cs b/src/Umbraco.Web/umbraco.presentation/item.cs index 3937b5675c..18437f5235 100644 --- a/src/Umbraco.Web/umbraco.presentation/item.cs +++ b/src/Umbraco.Web/umbraco.presentation/item.cs @@ -76,7 +76,7 @@ namespace umbraco //check for published content and get its value using that if (publishedContent != null && (publishedContent.HasProperty(_fieldName) || recursive)) { - var pval = publishedContent.Value(_fieldName, recurse: recursive); + var pval = publishedContent.Value(_fieldName, fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree }); var rval = pval == null ? string.Empty : pval.ToString(); _fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval; } @@ -96,7 +96,7 @@ namespace umbraco { if (publishedContent != null && (publishedContent.HasProperty(altFieldName) || recursive)) { - var pval = publishedContent.Value(altFieldName, recurse: recursive); + var pval = publishedContent.Value(altFieldName, fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree }); var rval = pval == null ? string.Empty : pval.ToString(); _fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval; } From a35f67ecef25af5befcbe52c62df6471ac66d460 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 24 Jul 2018 13:32:29 +0200 Subject: [PATCH 17/49] Refactoring --- src/Umbraco.Core/Constants-Content.cs | 24 +++- .../IPublishedValueFallback.cs | 53 ++++++-- .../NoopPublishedValueFallback.cs | 16 ++- .../PublishedContentLanguageVariantTests.cs | 28 +---- .../PublishedContent/PublishedContentTests.cs | 6 +- src/Umbraco.Tests/TestHelpers/BaseWebTest.cs | 2 +- .../PublishedValueFallback.cs | 116 +++++++++++------- .../PublishedValueLanguageFallback.cs | 90 -------------- src/Umbraco.Web/PublishedContentExtensions.cs | 20 ++- src/Umbraco.Web/PublishedElementExtensions.cs | 15 ++- ...nsion.cs => PublishedPropertyExtension.cs} | 4 +- .../Runtime/WebRuntimeComponent.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 3 +- src/Umbraco.Web/umbraco.presentation/item.cs | 4 +- 14 files changed, 168 insertions(+), 215 deletions(-) delete mode 100644 src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs rename src/Umbraco.Web/{PublishedContentPropertyExtension.cs => PublishedPropertyExtension.cs} (96%) diff --git a/src/Umbraco.Core/Constants-Content.cs b/src/Umbraco.Core/Constants-Content.cs index 4b8c383e6f..b9d0691454 100644 --- a/src/Umbraco.Core/Constants-Content.cs +++ b/src/Umbraco.Core/Constants-Content.cs @@ -11,11 +11,27 @@ /// Defines core supported content fall-back options when retrieving content property values. /// Defined as constants rather than enum to allow solution or package defined fall-back methods. /// - public static class FallbackMethods + public static class ValueFallback { - public const int None = 0; - public const int RecursiveTree = 1; - public const int FallbackLanguage = 2; + /// + /// No fallback at all. + /// + public const int None = -1; + + /// + /// Default fallback. + /// + public const int Default = 0; + + /// + /// Recurse up the tree. + /// + public const int Recurse = 1; + + /// + /// Fallback to other languages. + /// + public const int Language = 2; } } } diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index 59442e20bb..96a6c144fa 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -1,15 +1,44 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Models.PublishedContent +namespace Umbraco.Core.Models.PublishedContent { /// /// Provides a fallback strategy for getting values. /// - // fixme - IPublishedValueFallback is still WorkInProgress - // todo - properly document methods, etc - // todo - understand caching vs fallback (recurse etc) public interface IPublishedValueFallback { + // fixme discussions & challenges + // + // - what's with visitedLanguage? should be internal to fallback implementation + // so that should be the case now, with latest changes + // + // - should be as simple as + // model.Value("price", fallback: ValueFallback.Language); + // model.Value("name", fallback: ValueFallback.Recurse); + // + // so chaining things through an array of ints is not... convenient + // it feels like ppl could have ValueFallback.LanguageAndRecurse or something? + // + // - the fallback: parameter value must be open, so about anything can be passed to the IPublishedValueFallback + // we have it now, it's an integer + constants, cool + // + // - we need to be able to configure (via code for now) a default fallback policy? + // not! the default value of the fallback: parameter is 'default', not 'none', and if people + // want to implement a different default behavior, they have to override the fallback provider + // + // - currently, no policies on IPublishedProperty nor IPublishedElement, but some may apply (language) + // todo: implement + // + // - general defaultValue discussion: + // when HasValue is false, the converter may return something, eg an empty enumerable, even though + // defaultValue is null, so should we respect defaultValue only when it is not 'default'? + // todo: when defaultValue==default, and HasValue is false, still return GetValue to ensure this + // + // - (and...) + // ModelsBuilder model.Value(x => x.Price, ...) extensions need to be adjusted too + // + // - cache & perfs + // soon as ppl implement custom fallbacks, caching is a problem, so better just not cache + // OTOH we need to implement the readonly thing for languages + /// /// Gets a fallback value for a property. /// @@ -25,7 +54,7 @@ namespace Umbraco.Core.Models.PublishedContent /// At property level, property.GetValue() does *not* implement fallback, and one has to /// get property.Value() or property.Value{T}() to trigger fallback. /// - object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages); + object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue); /// /// Gets a fallback value for a property. @@ -43,7 +72,7 @@ namespace Umbraco.Core.Models.PublishedContent /// At property level, property.GetValue() does *not* implement fallback, and one has to /// get property.Value() or property.Value{T}() to trigger fallback. /// - T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages); + T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue); /// /// Gets a fallback value for a published element property. @@ -59,7 +88,7 @@ namespace Umbraco.Core.Models.PublishedContent /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. /// It can only fallback at element level (no recurse). /// - object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages); + object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue); /// /// Gets a fallback value for a published element property. @@ -76,7 +105,7 @@ namespace Umbraco.Core.Models.PublishedContent /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. /// It can only fallback at element level (no recurse). /// - T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages); + T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue); /// /// Gets a fallback value for a published content property. @@ -92,7 +121,7 @@ namespace Umbraco.Core.Models.PublishedContent /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. /// fixme explain & document priority + merge w/recurse? /// - object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages); + object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback); /// /// Gets a fallback value for a published content property. @@ -109,6 +138,6 @@ namespace Umbraco.Core.Models.PublishedContent /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. /// fixme explain & document priority + merge w/recurse? /// - T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages); + T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, int fallback); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs index a8d55176a3..9d74c4d8a2 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Models.PublishedContent +namespace Umbraco.Core.Models.PublishedContent { /// /// Provides a noop implementation for . @@ -11,21 +9,21 @@ namespace Umbraco.Core.Models.PublishedContent public class NoopPublishedValueFallback : IPublishedValueFallback { /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) => defaultValue; + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) => defaultValue; /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) => defaultValue; + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) => defaultValue; /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) => defaultValue; + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) => defaultValue; /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) => defaultValue; + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) => defaultValue; /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) => defaultValue; + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback) => defaultValue; /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) => defaultValue; + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, int fallback) => defaultValue; } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 7108824602..0b0f4dea51 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -167,7 +167,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - var value = content.Value("welcomeText", "es", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage }); + var value = content.Value("welcomeText", "es", fallback: Core.Constants.Content.ValueFallback.Language); Assert.AreEqual("Welcome", value); } @@ -175,7 +175,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - var value = content.Value("welcomeText", "it", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage }); + var value = content.Value("welcomeText", "it", fallback: Core.Constants.Content.ValueFallback.Language); Assert.AreEqual("Welcome", value); } @@ -183,7 +183,7 @@ namespace Umbraco.Tests.PublishedContent public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - var value = content.Value("welcomeText", "no", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage }); + var value = content.Value("welcomeText", "no", fallback: Core.Constants.Content.ValueFallback.Language); Assert.IsNull(value); } @@ -199,27 +199,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_Recursively() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); - var value = content.Value("welcomeText2", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.RecursiveTree }); - Assert.AreEqual("Welcome", value); - } - - [Test] - public void Can_Get_Content_With_Recursive_Priority() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); - var value = content.Value("welcomeText", "nl", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.RecursiveTree, Core.Constants.Content.FallbackMethods.FallbackLanguage }); - - // No Dutch value is directly assigned. Check has fallen back to Dutch value from parent. - Assert.AreEqual("Welkom", value); - } - - [Test] - public void Can_Get_Content_With_Fallback_Language_Priority() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); - var value = content.Value("welcomeText", "nl", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage, Core.Constants.Content.FallbackMethods.RecursiveTree }); - - // No Dutch value is directly assigned. Check has fallen back to English value from language variant. + var value = content.Value("welcomeText2", fallback: Core.Constants.Content.ValueFallback.Recurse); Assert.AreEqual("Welcome", value); } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 950bbf2283..93f4f3f242 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.PublishedContent Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); Container.RegisterSingleton(); - Container.RegisterSingleton(); + Container.RegisterSingleton(); var logger = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( @@ -336,8 +336,8 @@ namespace Umbraco.Tests.PublishedContent public void Get_Property_Value_Recursive() { var doc = GetNode(1174); - var rVal = doc.Value("testRecursive", fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree } ); - var nullVal = doc.Value("DoNotFindThis", fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree }); + var rVal = doc.Value("testRecursive", fallback: Constants.Content.ValueFallback.Recurse); + var nullVal = doc.Value("DoNotFindThis", fallback: Constants.Content.ValueFallback.Recurse); Assert.AreEqual("This is the recursive val", rVal); Assert.AreEqual(null, nullVal); } diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index 2f7fe8700b..5eea6bcf72 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.TestHelpers { base.Compose(); - Container.RegisterSingleton(); + Container.RegisterSingleton(); Container.RegisterSingleton(); } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 85b10be480..4136be40d8 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; +using ValueFallback = Umbraco.Core.Constants.Content.ValueFallback; namespace Umbraco.Web.Models.PublishedContent { @@ -9,90 +12,72 @@ namespace Umbraco.Web.Models.PublishedContent /// public class PublishedValueFallback : IPublishedValueFallback { - // kinda reproducing what was available in v7 + private readonly ILocalizationService _localizationService; + + /// + /// Initializes a new instance of the class. + /// + /// + public PublishedValueFallback(ILocalizationService localizationService) + { + _localizationService = localizationService; + } /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) { // no fallback here return defaultValue; } /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) { // no fallback here return defaultValue; } /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) { // no fallback here return defaultValue; } /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) { // no fallback here return defaultValue; } /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback) { // is that ok? - return GetValue(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages); + return GetValue(content, alias, culture, segment, defaultValue, fallback); } /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) + public virtual T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, int fallback) { - if (fallbackMethods == null) + switch (fallback) { - return defaultValue; - } - - foreach (var fallbackMethod in fallbackMethods) - { - if (TryGetValueWithFallbackMethod(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages, fallbackMethod, out T value)) - { - return value; - } - } - - return defaultValue; - } - - protected virtual bool TryGetValueWithFallbackMethod(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages, int fallbackMethod, out T value) - { - value = defaultValue; - switch (fallbackMethod) - { - case Core.Constants.Content.FallbackMethods.None: - return false; - case Core.Constants.Content.FallbackMethods.RecursiveTree: - return TryGetValueWithRecursiveTree(content, alias, culture, segment, defaultValue, out value); + case ValueFallback.None: + case ValueFallback.Default: + return defaultValue; + case ValueFallback.Recurse: + return TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out var value1) ? value1 : defaultValue; + case ValueFallback.Language: + return TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value2) ? value2 : defaultValue; default: - throw new NotSupportedException($"Fallback method with indentifying number {fallbackMethod} is not supported within {GetType().Name}."); + throw new NotSupportedException($"Fallback {GetType().Name} does not support policy code '{fallback}'."); } } - protected static bool TryGetValueWithRecursiveTree(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value) + // tries to get a value, recursing the tree + protected static bool TryGetValueWithRecursiveFallback(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value) { - // Implement recursion as it was implemented in PublishedContentBase - - // fixme caching? - // - // all caches were using PublishedContentBase.GetProperty(alias, recurse) to get the property, - // then, - // NuCache.PublishedContent was storing the property in GetAppropriateCache() with key "NuCache.Property.Recurse[" + DraftOrPub(previewing) + contentUid + ":" + typeAlias + "]"; - // XmlPublishedContent was storing the property in _cacheProvider with key $"XmlPublishedCache.PublishedContentCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; - // DictionaryPublishedContent was storing the property in _cacheProvider with key $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; - // - // at the moment, caching has been entirely removed, until we better understand caching + fallback - IPublishedProperty property = null; // if we are here, content's property has no value IPublishedProperty noValueProperty = null; do @@ -125,5 +110,44 @@ namespace Umbraco.Web.Models.PublishedContent value = defaultValue; return false; } + + // tries to get a value, falling back onto other languages + private bool TryGetValueWithLanguageFallback(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value) + { + value = defaultValue; + + if (culture.IsNullOrWhiteSpace()) return false; + + var visited = new HashSet(); + + // fixme + // _localizationService.GetXxx() is expensive, it deep clones objects + // we want _localizationService.GetReadOnlyXxx() returning IReadOnlyLanguage which cannot be saved back = no need to clone + + var language = _localizationService.GetLanguageByIsoCode(culture); + if (language == null) return false; + + while (true) + { + if (language.FallbackLanguageId == null) return false; + + var language2Id = language.FallbackLanguageId.Value; + if (visited.Contains(language2Id)) return false; + visited.Add(language2Id); + + var language2 = _localizationService.GetLanguageById(language2Id); + if (language2 == null) return false; + var culture2 = language2.IsoCode; + + if (content.HasValue(alias, culture2, segment)) + { + value = content.Value(alias, culture2, segment); + return true; + } + + language = language2; + } + } + } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs deleted file mode 100644 index e94a8559a0..0000000000 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; - -namespace Umbraco.Web.Models.PublishedContent -{ - /// - /// Provides a default implementation for that allows - /// for use of fall-back languages - /// - /// - /// Inherits from that implments what was available in v7. - /// - public class PublishedValueLanguageFallback : PublishedValueFallback - { - private readonly ILocalizationService _localizationService; - - public PublishedValueLanguageFallback(ServiceContext services) - { - _localizationService = services.LocalizationService; - } - - protected override bool TryGetValueWithFallbackMethod(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages, int fallbackMethod, out T value) - { - value = defaultValue; - switch (fallbackMethod) - { - case Core.Constants.Content.FallbackMethods.None: - return false; - case Core.Constants.Content.FallbackMethods.RecursiveTree: - return TryGetValueWithRecursiveTree(content, alias, culture, segment, defaultValue, out value); - case Core.Constants.Content.FallbackMethods.FallbackLanguage: - return TryGetValueWithFallbackLanguage(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages, out value); - default: - throw new NotSupportedException($"Fallback method with indentifying number {fallbackMethod} is not supported within {GetType().Name}."); - } - } - - private bool TryGetValueWithFallbackLanguage(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages, out T value) - { - value = defaultValue; - if (string.IsNullOrEmpty(culture)) - { - return false; - } - - var language = _localizationService.GetLanguageByIsoCode(culture); - if (language.FallbackLanguageId.HasValue == false) - { - return false; - } - - if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value)) - { - return false; - } - - visitedLanguages.Add(language.FallbackLanguageId.Value); - - var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); - value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, fallbackMethods.ToArray(), visitedLanguages); - if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) - { - return true; - } - - return false; - } - - private static bool AlreadyVisitedLanguage(ICollection visitedLanguages, int fallbackLanguageId) - { - return visitedLanguages.Contains(fallbackLanguageId); - } - - private ILanguage GetLanguageById(int id) - { - return _localizationService.GetLanguageById(id); - } - - private static bool ValueIsNotNullEmptyOrDefault(T value, T defaultValue) - { - return value != null && - string.IsNullOrEmpty(value.ToString()) == false && - value.Equals(defaultValue) == false; - } - } -} diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 0bea71358a..64738aa923 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -153,18 +153,16 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. - /// Options for fall-back if content not found. - /// A list of cultures already visited in looking for a value via a fall-back method. + /// Optional fallback strategy. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. - public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, - int[] fallbackMethods = null, ICollection visitedLanguages = null) + public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, int fallback = 0) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages ?? new List()); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback); } #endregion @@ -180,24 +178,20 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. - /// Options for fall-back if content not found. - /// A list of cultures already visited in looking for a value via a fall-back method. + /// Optional fallback strategy. /// The value of the content's property identified by the alias, converted to the specified type. - /// - public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, - int[] fallbackMethods = null, ICollection visitedLanguages = null) + public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, int fallback = 0) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages ?? new List()); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback); } // fixme - .Value() refactoring - in progress - public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", - int[] fallbackMethods = null, ICollection visitedLanguages = null) + public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", int fallback = 0) { var aliasesA = aliases.Split(','); if (aliasesA.Length == 0) diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index b0d2826df4..51f0b6c0dc 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -100,7 +100,6 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. - /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -108,14 +107,19 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default, ICollection visitedLanguages = null) + public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages ?? new List()); + // fixme defaultValue is a problem here + // assuming the value may return as an IEnumerable and no defaultValue is provided, then defaultValue is null + // and if HasValue is false, what we get is 'null' - but the converter may instead have been able to return an + // empty enumerable, which would be way nicer - so we need a way to tell that 'no defaultValue has been provided'? + + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); } #endregion @@ -131,7 +135,6 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. - /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, converted to the specified type. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -139,14 +142,14 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default, ICollection visitedLanguages = null) + public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages ?? new List()); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); } #endregion diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedPropertyExtension.cs similarity index 96% rename from src/Umbraco.Web/PublishedContentPropertyExtension.cs rename to src/Umbraco.Web/PublishedPropertyExtension.cs index fdfd772ce7..bce13d30b4 100644 --- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedPropertyExtension.cs @@ -16,12 +16,12 @@ namespace Umbraco.Web #region Value - public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default, ICollection visitedLanguages = null) + public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default) { if (property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(property, culture, segment, defaultValue, visitedLanguages ?? new List()); + return PublishedValueFallback.GetValue(property, culture, segment, defaultValue); } #endregion diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 78ddb935a0..03ba763527 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -199,7 +199,7 @@ namespace Umbraco.Web.Runtime composition.Container.Register(_ => GlobalHost.ConnectionManager.GetHubContext(), new PerContainerLifetime()); // register properties fallback - composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); } internal void Initialize( diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index af2170123a..1d56ed5c7c 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -263,7 +263,6 @@ - @@ -838,7 +837,7 @@ - + diff --git a/src/Umbraco.Web/umbraco.presentation/item.cs b/src/Umbraco.Web/umbraco.presentation/item.cs index 14835b278f..d89733044d 100644 --- a/src/Umbraco.Web/umbraco.presentation/item.cs +++ b/src/Umbraco.Web/umbraco.presentation/item.cs @@ -78,7 +78,7 @@ namespace umbraco //check for published content and get its value using that if (publishedContent != null && (publishedContent.HasProperty(_fieldName) || recursive)) { - var pval = publishedContent.Value(_fieldName, fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree }); + var pval = publishedContent.Value(_fieldName, fallback: Constants.Content.ValueFallback.Recurse); var rval = pval == null ? string.Empty : pval.ToString(); _fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval; } @@ -98,7 +98,7 @@ namespace umbraco { if (publishedContent != null && (publishedContent.HasProperty(altFieldName) || recursive)) { - var pval = publishedContent.Value(altFieldName, fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree }); + var pval = publishedContent.Value(altFieldName, fallback: Constants.Content.ValueFallback.Recurse); var rval = pval == null ? string.Empty : pval.ToString(); _fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval; } From 85bdc8f14d4f092c96a25de140ff1b0d9aa3c913 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 28 Jul 2018 07:56:23 +0200 Subject: [PATCH 18/49] Updated XML documentation on IPublisedValueCallback methods --- .../Models/PublishedContent/IPublishedValueFallback.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index 96a6c144fa..0c826e6bd5 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -115,6 +115,7 @@ /// The requested culture. /// The requested segment. /// An optional default value. + /// Integer value defining method to use for fallback when content not found /// A fallback value, or null. /// /// This method is called whenever getting the property value for the specified alias, culture and @@ -132,6 +133,7 @@ /// The requested culture. /// The requested segment. /// An optional default value. + /// Integer value defining method to use for fallback when content not found /// A fallback value, or null. /// /// This method is called whenever getting the property value for the specified alias, culture and From b608ea832277d645664621ddec1f87211ecb282b Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 28 Jul 2018 07:58:22 +0200 Subject: [PATCH 19/49] Restored service context as being DIed into PublishedValueFallback. Whilst only the LocalizationService service is needed, not injecting this via the service context was breaking the unit tests. If there's a way around this so we can use the mocked ILocalizationService in tests then this change can be reverted. --- .../Models/PublishedContent/PublishedValueFallback.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 4136be40d8..71bbe9577a 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -17,10 +17,10 @@ namespace Umbraco.Web.Models.PublishedContent /// /// Initializes a new instance of the class. /// - /// - public PublishedValueFallback(ILocalizationService localizationService) + /// + public PublishedValueFallback(ServiceContext serviceContext) { - _localizationService = localizationService; + _localizationService = serviceContext.LocalizationService; } /// From 49c6212cb63547f1530b8da931853ef938d5374e Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 28 Jul 2018 08:11:48 +0200 Subject: [PATCH 20/49] Implemented constants, tests and functionality for trying one fallback method and then another --- src/Umbraco.Core/Constants-Content.cs | 10 ++++ .../PublishedContentLanguageVariantTests.cs | 48 +++++++++++++++---- .../PublishedValueFallback.cs | 8 ++++ 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Constants-Content.cs b/src/Umbraco.Core/Constants-Content.cs index b9d0691454..3f12ece6dc 100644 --- a/src/Umbraco.Core/Constants-Content.cs +++ b/src/Umbraco.Core/Constants-Content.cs @@ -32,6 +32,16 @@ /// Fallback to other languages. /// public const int Language = 2; + + /// + /// Recurse up the tree. If content not found, fallback to other languages. + /// + public const int RecurseThenLanguage = 3; + + /// + /// Fallback to other languages. If content not found, recurse up the tree. + /// + public const int LanguageThenRecurse = 4; } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 0b0f4dea51..d5e01fd424 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Moq; @@ -66,9 +67,9 @@ namespace Umbraco.Tests.PublishedContent var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); var prop1 = new SolidPublishedPropertyWithLanguageVariants - { - Alias = "welcomeText", - }; + { + Alias = "welcomeText", + }; prop1.SetSourceValue("en-US", "Welcome", true); prop1.SetValue("en-US", "Welcome", true); prop1.SetSourceValue("de", "Willkommen"); @@ -77,16 +78,16 @@ namespace Umbraco.Tests.PublishedContent prop1.SetValue("nl", "Welkom"); var prop2 = new SolidPublishedPropertyWithLanguageVariants - { - Alias = "welcomeText2", - }; + { + Alias = "welcomeText2", + }; prop2.SetSourceValue("en-US", "Welcome", true); prop2.SetValue("en-US", "Welcome", true); var prop3 = new SolidPublishedPropertyWithLanguageVariants - { - Alias = "welcomeText", - }; + { + Alias = "welcomeText", + }; prop3.SetSourceValue("en-US", "Welcome", true); prop3.SetValue("en-US", "Welcome", true); @@ -202,5 +203,32 @@ namespace Umbraco.Tests.PublishedContent var value = content.Value("welcomeText2", fallback: Core.Constants.Content.ValueFallback.Recurse); Assert.AreEqual("Welcome", value); } + + [Test] + public void Can_Get_Content_With_Recursive_Priority() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText", "nl", fallback: Core.Constants.Content.ValueFallback.RecurseThenLanguage); + + // No Dutch value is directly assigned. Check has fallen back to Dutch value from parent. + Assert.AreEqual("Welkom", value); + } + + [Test] + public void Can_Get_Content_With_Fallback_Language_Priority() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var value = content.Value("welcomeText", "nl", fallback: Core.Constants.Content.ValueFallback.LanguageThenRecurse); + + // No Dutch value is directly assigned. Check has fallen back to English value from language variant. + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Throws_For_Non_Supported_Fallback() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + Assert.Throws(() => content.Value("welcomeText", "nl", fallback: 999)); + } } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 71bbe9577a..02e809025a 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -70,6 +70,14 @@ namespace Umbraco.Web.Models.PublishedContent return TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out var value1) ? value1 : defaultValue; case ValueFallback.Language: return TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value2) ? value2 : defaultValue; + case ValueFallback.RecurseThenLanguage: + return TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out var value3) + ? value3 + : TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value4) ? value4 : defaultValue; + case ValueFallback.LanguageThenRecurse: + return TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value5) + ? value5 + : TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out var value6) ? value6 : defaultValue; default: throw new NotSupportedException($"Fallback {GetType().Name} does not support policy code '{fallback}'."); } From c61f041d069ea8a1be9e939a47da8316eb6ec885 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 28 Jul 2018 08:39:02 +0200 Subject: [PATCH 21/49] Added fallback by language option to IPublishedElement and IPublishedProperty --- .../IPublishedValueFallback.cs | 12 +- .../NoopPublishedValueFallback.cs | 8 +- .../PublishedValueFallback.cs | 116 +++++++++++++++--- src/Umbraco.Web/PublishedElementExtensions.cs | 10 +- src/Umbraco.Web/PublishedPropertyExtension.cs | 8 +- 5 files changed, 123 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index 0c826e6bd5..c67df36c4f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -46,6 +46,7 @@ /// The requested culture. /// The requested segment. /// An optional default value. + /// Integer value defining method to use for fallback when content not found /// A fallback value, or null. /// /// This method is called whenever property.Value(culture, segment, defaultValue) is called, and @@ -54,7 +55,7 @@ /// At property level, property.GetValue() does *not* implement fallback, and one has to /// get property.Value() or property.Value{T}() to trigger fallback. /// - object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue); + object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, int fallback); /// /// Gets a fallback value for a property. @@ -64,6 +65,7 @@ /// The requested culture. /// The requested segment. /// An optional default value. + /// Integer value defining method to use for fallback when content not found /// A fallback value, or null. /// /// This method is called whenever property.Value{T}(culture, segment, defaultValue) is called, and @@ -72,7 +74,7 @@ /// At property level, property.GetValue() does *not* implement fallback, and one has to /// get property.Value() or property.Value{T}() to trigger fallback. /// - T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue); + T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, int fallback); /// /// Gets a fallback value for a published element property. @@ -82,13 +84,14 @@ /// The requested culture. /// The requested segment. /// An optional default value. + /// Integer value defining method to use for fallback when content not found /// A fallback value, or null. /// /// This method is called whenever getting the property value for the specified alias, culture and /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. /// It can only fallback at element level (no recurse). /// - object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue); + object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, int fallback); /// /// Gets a fallback value for a published element property. @@ -99,13 +102,14 @@ /// The requested culture. /// The requested segment. /// An optional default value. + /// Integer value defining method to use for fallback when content not found /// A fallback value, or null. /// /// This method is called whenever getting the property value for the specified alias, culture and /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. /// It can only fallback at element level (no recurse). /// - T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue); + T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, int fallback); /// /// Gets a fallback value for a published content property. diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs index 9d74c4d8a2..d920cefb24 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -9,16 +9,16 @@ public class NoopPublishedValueFallback : IPublishedValueFallback { /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) => defaultValue; + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, int fallback) => defaultValue; /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) => defaultValue; + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, int fallback) => defaultValue; /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) => defaultValue; + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, int fallback) => defaultValue; /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) => defaultValue; + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, int fallback) => defaultValue; /// public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback) => defaultValue; diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 02e809025a..bfcd339650 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -24,31 +24,45 @@ namespace Umbraco.Web.Models.PublishedContent } /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, int fallback) { - // no fallback here - return defaultValue; + return GetValue(property, culture, segment, defaultValue, fallback); } /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, int fallback) { - // no fallback here - return defaultValue; + switch (fallback) + { + case ValueFallback.None: + case ValueFallback.Default: + return defaultValue; + case ValueFallback.Language: + return TryGetValueWithLanguageFallback(property, culture, segment, defaultValue, out var value2) ? value2 : defaultValue; + default: + throw NotSupportedFallbackMethod(fallback); + } } /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, int fallback) { - // no fallback here - return defaultValue; + return GetValue(content, alias, culture, segment, defaultValue, fallback); } /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, int fallback) { - // no fallback here - return defaultValue; + switch (fallback) + { + case ValueFallback.None: + case ValueFallback.Default: + return defaultValue; + case ValueFallback.Language: + return TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value2) ? value2 : defaultValue; + default: + throw NotSupportedFallbackMethod(fallback); + } } /// @@ -79,12 +93,17 @@ namespace Umbraco.Web.Models.PublishedContent ? value5 : TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out var value6) ? value6 : defaultValue; default: - throw new NotSupportedException($"Fallback {GetType().Name} does not support policy code '{fallback}'."); + throw NotSupportedFallbackMethod(fallback); } } + private NotSupportedException NotSupportedFallbackMethod(int fallback) + { + return new NotSupportedException($"Fallback {GetType().Name} does not support policy code '{fallback}'."); + } + // tries to get a value, recursing the tree - protected static bool TryGetValueWithRecursiveFallback(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value) + private static bool TryGetValueWithRecursiveFallback(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value) { IPublishedProperty property = null; // if we are here, content's property has no value IPublishedProperty noValueProperty = null; @@ -119,6 +138,74 @@ namespace Umbraco.Web.Models.PublishedContent return false; } + // tries to get a value, falling back onto other languages + private bool TryGetValueWithLanguageFallback(IPublishedProperty property, string culture, string segment, T defaultValue, out T value) + { + value = defaultValue; + + if (culture.IsNullOrWhiteSpace()) return false; + + var visited = new HashSet(); + + var language = _localizationService.GetLanguageByIsoCode(culture); + if (language == null) return false; + + while (true) + { + if (language.FallbackLanguageId == null) return false; + + var language2Id = language.FallbackLanguageId.Value; + if (visited.Contains(language2Id)) return false; + visited.Add(language2Id); + + var language2 = _localizationService.GetLanguageById(language2Id); + if (language2 == null) return false; + var culture2 = language2.IsoCode; + + if (property.HasValue(culture2, segment)) + { + value = property.Value(culture2, segment); + return true; + } + + language = language2; + } + } + + // tries to get a value, falling back onto other languages + private bool TryGetValueWithLanguageFallback(IPublishedElement content, string alias, string culture, string segment, T defaultValue, out T value) + { + value = defaultValue; + + if (culture.IsNullOrWhiteSpace()) return false; + + var visited = new HashSet(); + + var language = _localizationService.GetLanguageByIsoCode(culture); + if (language == null) return false; + + while (true) + { + if (language.FallbackLanguageId == null) return false; + + var language2Id = language.FallbackLanguageId.Value; + if (visited.Contains(language2Id)) return false; + visited.Add(language2Id); + + var language2 = _localizationService.GetLanguageById(language2Id); + if (language2 == null) return false; + var culture2 = language2.IsoCode; + + if (content.HasValue(alias, culture2, segment)) + { + value = content.Value(alias, culture2, segment); + return true; + } + + language = language2; + } + } + // tries to get a value, falling back onto other languages private bool TryGetValueWithLanguageFallback(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value) { @@ -156,6 +243,5 @@ namespace Umbraco.Web.Models.PublishedContent language = language2; } } - } } diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index 51f0b6c0dc..eea92f4c6c 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -100,6 +100,7 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. + /// Optional fallback strategy. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -107,7 +108,7 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default) + public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default, int fallback = 0) { var property = content.GetProperty(alias); @@ -119,7 +120,7 @@ namespace Umbraco.Web // and if HasValue is false, what we get is 'null' - but the converter may instead have been able to return an // empty enumerable, which would be way nicer - so we need a way to tell that 'no defaultValue has been provided'? - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback); } #endregion @@ -135,6 +136,7 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. + /// Optional fallback strategy. /// The value of the content's property identified by the alias, converted to the specified type. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -142,14 +144,14 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default) + public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default, int fallback = 0) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback); } #endregion diff --git a/src/Umbraco.Web/PublishedPropertyExtension.cs b/src/Umbraco.Web/PublishedPropertyExtension.cs index bce13d30b4..6632076022 100644 --- a/src/Umbraco.Web/PublishedPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedPropertyExtension.cs @@ -16,19 +16,19 @@ namespace Umbraco.Web #region Value - public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default) + public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default, int fallback = 0) { if (property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(property, culture, segment, defaultValue); + return PublishedValueFallback.GetValue(property, culture, segment, defaultValue, fallback); } #endregion #region Value - public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default, ICollection visitedLanguages = null) + public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default, int fallback = 0) { // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below) // but we have no way to tell whether default value is specified or not - we could do it with overloads, but then defaultValue @@ -65,7 +65,7 @@ namespace Umbraco.Web //convert = source.TryConvertTo(); //if (convert.Success) return convert.Result; - return defaultValue; + return PublishedValueFallback.GetValue(property, culture, segment, defaultValue, fallback); } #endregion From 68d083249baa95efd5f7090ae8f3837246d7ee7c Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 6 Sep 2018 16:09:31 +0200 Subject: [PATCH 22/49] Fix merge --- src/Umbraco.Web/Editors/ContentController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 09126b80ee..d9ef3b5ed5 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -860,7 +860,7 @@ namespace Umbraco.Web.Editors var canPublish = true; //validate any mandatory variants that are not in the list - var mandatoryLangs = Mapper.Map, IEnumerable>(_allLangs.Value.Values).Where(x => x.Mandatory); + var mandatoryLangs = Mapper.Map, IEnumerable>(_allLangs.Value.Values).Where(x => x.IsMandatory); foreach (var lang in mandatoryLangs) { From 25c87d76b8cbda43a71ade65a7cb49515efb32d9 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 13 Sep 2018 14:59:45 +0200 Subject: [PATCH 23/49] Fix merge --- .../Migrations/Install/DatabaseDataCreator.cs | 2 +- .../Migrations/Upgrade/UmbracoPlan.cs | 11 +-- .../V_8_0_0/UpdateDefaultMandatoryLanguage.cs | 6 +- .../Persistence/Dtos/LanguageDto.cs | 2 +- .../Persistence/Factories/LanguageFactory.cs | 4 +- .../Implement/LanguageRepository.cs | 15 ++++- .../Services/Implement/LocalizationService.cs | 24 +++++++ src/Umbraco.Web/Editors/LanguageController.cs | 67 ++++++++----------- 8 files changed, 78 insertions(+), 53 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs index bfb8705480..5ec259ade4 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs @@ -232,7 +232,7 @@ namespace Umbraco.Core.Migrations.Install private void CreateLanguageData() { - _database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "English (United States)", IsDefaultVariantLanguage = true }); + _database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "English (United States)", IsDefault = true }); } private void CreateContentChildTypeData() diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index fb87a1ff4f..036fc99fa4 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -118,17 +118,18 @@ namespace Umbraco.Core.Migrations.Upgrade Chain("{EB34B5DC-BB87-4005-985E-D983EA496C38}"); // from 7.12.0 Chain("{517CE9EA-36D7-472A-BF4B-A0D6FB1B8F89}"); // from 7.12.0 Chain("{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}"); // from 7.12.0 - //Chain("{2C87AA47-D1BC-4ECB-8A73-2D8D1046C27F}"); // stephan added that one = merge conflict, remove Chain("{8B14CEBD-EE47-4AAD-A841-93551D917F11}"); // add andy's after others, with a new target state From("{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}") // and provide a path out of andy's - .CopyChain("{39E5B1F7-A50B-437E-B768-1723AEC45B65}", "{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}", "{8B14CEBD-EE47-4AAD-A841-93551D917F11}"); // to final + .CopyChain("{39E5B1F7-A50B-437E-B768-1723AEC45B65}", "{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}", "{8B14CEBD-EE47-4AAD-A841-93551D917F11}"); // to next // resume at {8B14CEBD-EE47-4AAD-A841-93551D917F11} ... - Chain("{guid1}"); // add stephan's after others, with a new target state + + Chain("{5F4597F4-A4E0-4AFE-90B5-6D2F896830EB}"); // add stephan's after others, with a new target state From("{2C87AA47-D1BC-4ECB-8A73-2D8D1046C27F}") // and provide a path out of stephan's - .Chain("{guid1}"); - #error is this OK? + .Chain("{5F4597F4-A4E0-4AFE-90B5-6D2F896830EB}"); // to next + // resume at {5F4597F4-A4E0-4AFE-90B5-6D2F896830EB} ... + //FINAL diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs index dd5fe7c369..b965bc71d2 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs @@ -26,7 +26,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 var defaultId = int.MaxValue; foreach (var dto in dtos) { - if (dto.IsDefaultVariantLanguage) + if (dto.IsDefault) { defaultId = dto.Id; break; @@ -38,8 +38,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 // update, so that language with that id is now default and mandatory var updateDefault = Sql() .Update(u => u - .Set(x => x.IsDefaultVariantLanguage, true) - .Set(x => x.Mandatory, true)) + .Set(x => x.IsDefault, true) + .Set(x => x.IsMandatory, true)) .Where(x => x.Id == defaultId); Database.Execute(updateDefault); diff --git a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs index 5f3247bc49..488390f985 100644 --- a/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/LanguageDto.cs @@ -39,7 +39,7 @@ namespace Umbraco.Core.Persistence.Dtos /// [Column("isDefaultVariantLang")] [Constraint(Default = "0")] - public bool IsDefaultVariantLanguage { get; set; } + public bool IsDefault { get; set; } /// /// Gets or sets a value indicating whether the language is mandatory. diff --git a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs index 9be2409a5e..ad58c5b570 100644 --- a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Factories { CultureName = dto.CultureName, Id = dto.Id, - IsDefault = dto.IsDefaultVariantLanguage, + IsDefault = dto.IsDefault, IsMandatory = dto.IsMandatory, FallbackLanguageId = dto.FallbackLanguageId }; @@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence.Factories { CultureName = entity.CultureName, IsoCode = entity.IsoCode, - IsDefaultVariantLanguage = entity.IsDefault, + IsDefault = entity.IsDefault, IsMandatory = entity.IsMandatory, FallbackLanguageId = entity.FallbackLanguageId }; diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 97535fe07c..2b3674700b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -136,9 +136,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // set all other entities to non-default // safe (no race cond) because the service locks languages var setAllDefaultToFalse = Sql() - .Update(u => u.Set(x => x.IsDefaultVariantLanguage, false)); + .Update(u => u.Set(x => x.IsDefault, false)); Database.Execute(setAllDefaultToFalse); } + + // fallback cycles are detected at service level + // insert var dto = LanguageFactory.BuildDto(entity); var id = Convert.ToInt32(Database.Insert(dto)); @@ -178,6 +181,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement throw new InvalidOperationException($"Cannot save the default language ({entity.IsoCode}) as non-default. Make another language the default language instead."); } + // fallback cycles are detected at service level + // update var dto = LanguageFactory.BuildDto(entity); Database.Update(dto); @@ -199,8 +204,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode})."); // We need to remove any references to the language if it's being used as a fall-back from other ones - #error rewirte - Database.Execute(Sql().Update(u => u.Set(x => x.FallbackLanguageId, null)).Where(x => x.FallbackLanguageId == entity.Id)); + var clearFallbackLanguage = Sql() + .Update(u => u + .Set(x => x.FallbackLanguageId, null)) + .Where(x => x.FallbackLanguageId == entity.Id); + + Database.Execute(clearFallbackLanguage); // delete base.PersistDeletedItem(entity); diff --git a/src/Umbraco.Core/Services/Implement/LocalizationService.cs b/src/Umbraco.Core/Services/Implement/LocalizationService.cs index ec328bdb9d..49a764b533 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizationService.cs @@ -363,6 +363,16 @@ namespace Umbraco.Core.Services.Implement // write-lock languages to guard against race conds when dealing with default language scope.WriteLock(Constants.Locks.Languages); + // look for cycles - within write-lock + if (language.FallbackLanguageId.HasValue) + { + var languages = _languageRepository.GetMany().ToDictionary(x => x.Id, x => x); + if (!languages.ContainsKey(language.FallbackLanguageId.Value)) + throw new InvalidOperationException($"Cannot save language {language.IsoCode} with fallback id={language.FallbackLanguageId.Value} which is not a valid language id."); + if (CreatesCycle(language, languages)) + throw new InvalidOperationException($"Cannot save language {language.IsoCode} with fallback {languages[language.FallbackLanguageId.Value].IsoCode} as it would create a fallback cycle."); + } + var saveEventArgs = new SaveEventArgs(language); if (scope.Events.DispatchCancelable(SavingLanguage, this, saveEventArgs)) { @@ -380,6 +390,20 @@ namespace Umbraco.Core.Services.Implement } } + private bool CreatesCycle(ILanguage language, IDictionary languages) + { + // a new language is not referenced yet, so cannot be part of a cycle + if (!language.HasIdentity) return false; + + var id = language.FallbackLanguageId; + while (true) // assuming languages does not already contains a cycle, this must end + { + if (!id.HasValue) return false; // no fallback means no cycle + if (id.Value == language.Id) return true; // back to language = cycle! + id = languages[id.Value].FallbackLanguageId; // else keep chaining + } + } + /// /// Deletes a by removing it (but not its usages) from the db /// diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index 5a6848dea1..a3e613670b 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -77,11 +77,8 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message)); } - if (langs.Any(x => x.FallbackLanguageId.HasValue && x.FallbackLanguageId.Value == language.Id)) - { - var message = $"Language '{language.CultureName}' is defined as a fall-back language for one or more other languages, and so cannot be deleted."; - throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message)); - } + // service is happy deleting a language that's fallback for another language, + // will just remove it - so no need to check here Services.LocalizationService.Delete(language); @@ -121,7 +118,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); } - //create it + // create it (creating a new language cannot create a fallback cycle) var newLang = new Core.Models.Language(culture.Name) { CultureName = culture.DisplayName, @@ -147,46 +144,40 @@ namespace Umbraco.Web.Editors existing.IsDefault = language.IsDefault; existing.FallbackLanguageId = language.FallbackLanguageId; - string selectedFallbackLanguageCultureName; - if (DoesUpdatedFallbackLanguageCreateACircularPath(existing, out selectedFallbackLanguageCultureName)) + // modifying an existing language can create a fallback, verify + // note that the service will check again, dealing with race conds + if (existing.FallbackLanguageId.HasValue) { - ModelState.AddModelError("FallbackLanguage", "The selected fall back language '" + selectedFallbackLanguageCultureName + "' would create a circular path."); - throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + var languages = Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x); + if (!languages.ContainsKey(existing.FallbackLanguageId.Value)) + { + ModelState.AddModelError("FallbackLanguage", "The selected fall back language does not exist."); + throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + } + if (CreatesCycle(existing, languages)) + { + ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existing.FallbackLanguageId.Value].IsoCode} would create a circular path."); + throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + } } Services.LocalizationService.Save(existing); return Mapper.Map(existing); - } - - private bool DoesUpdatedFallbackLanguageCreateACircularPath(ILanguage language, out string selectedFallbackLanguageCultureName) - { - if (language.FallbackLanguageId.HasValue == false) - { - selectedFallbackLanguageCultureName = string.Empty; - return false; - } - - var languages = Services.LocalizationService.GetAllLanguages().ToArray(); - var fallbackLanguageId = language.FallbackLanguageId; - while (fallbackLanguageId.HasValue) - { - if (fallbackLanguageId.Value == language.Id) - { - // We've found the current language in the path of fall back languages, so we have a circular path. - selectedFallbackLanguageCultureName = GetLanguageFromCollectionById(languages, fallbackLanguageId.Value).CultureName; - return true; - } - - fallbackLanguageId = GetLanguageFromCollectionById(languages, fallbackLanguageId.Value).FallbackLanguageId; - } - - selectedFallbackLanguageCultureName = string.Empty; - return false; } - private static ILanguage GetLanguageFromCollectionById(IEnumerable languages, int id) + // see LocalizationService + private bool CreatesCycle(ILanguage language, IDictionary languages) { - return languages.Single(x => x.Id == id); + // a new language is not referenced yet, so cannot be part of a cycle + if (!language.HasIdentity) return false; + + var id = language.FallbackLanguageId; + while (true) // assuming languages does not already contains a cycle, this must end + { + if (!id.HasValue) return false; // no fallback means no cycle + if (id.Value == language.Id) return true; // back to language = cycle! + id = languages[id.Value].FallbackLanguageId; // else keep chaining + } } } } From 59c0e3f9886fb79d489b4d08b990499b902a9e4b Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 2 Oct 2018 11:45:06 +0100 Subject: [PATCH 24/49] Uses underscorejs omit to remove the 'apps' from the object we copy to avoid a never ending nested tree of apps in the JSON --- .../components/content/umbvariantcontenteditors.directive.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js index 46d0a1c5c1..a3a212a603 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js @@ -186,14 +186,15 @@ } else { vm.openVariants[editorIndex] = variant.language.culture; } - + } //then assign the variant to a view model to the content app var contentApp = _.find(variant.apps, function (a) { return a.alias === "umbContent"; }); - contentApp.viewModel = variant; + + contentApp.viewModel = _.omit(variant, 'apps'); // make sure the same app it set to active in the new variant if(activeAppAlias) { From e2eaa1b9ddc9b1a81265efca5e2b2da7f8d64962 Mon Sep 17 00:00:00 2001 From: elitsa Date: Tue, 2 Oct 2018 14:08:45 +0200 Subject: [PATCH 25/49] Change the location of the Developer section's dashboards markups and controllers into the respective Settings folder . --- .../{developer => settings}/developerdashboardvideos.html | 0 .../dashboard/{developer => settings}/examinemanagement.html | 0 .../dashboard/{developer => settings}/examinemgmt.controller.js | 0 .../dashboard/{developer => settings}/healthcheck.controller.js | 0 .../src/views/dashboard/{developer => settings}/healthcheck.html | 0 .../views/dashboard/{developer => settings}/nucache.controller.js | 0 .../src/views/dashboard/{developer => settings}/nucache.html | 0 .../{developer => settings}/publishedstatus.controller.js | 0 .../views/dashboard/{developer => settings}/publishedstatus.html | 0 .../{developer => settings}/xmldataintegrityreport.controller.js | 0 .../dashboard/{developer => settings}/xmldataintegrityreport.html | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/developerdashboardvideos.html (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/examinemanagement.html (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/examinemgmt.controller.js (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/healthcheck.controller.js (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/healthcheck.html (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/nucache.controller.js (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/nucache.html (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/publishedstatus.controller.js (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/publishedstatus.html (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/xmldataintegrityreport.controller.js (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => settings}/xmldataintegrityreport.html (100%) diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/developerdashboardvideos.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/developerdashboardvideos.html diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemanagement.html rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemgmt.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemgmt.controller.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemgmt.controller.js rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemgmt.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/healthcheck.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/healthcheck.controller.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/healthcheck.controller.js rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/healthcheck.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/healthcheck.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/healthcheck.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/healthcheck.html rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/healthcheck.html diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/nucache.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.controller.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/nucache.controller.js rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/nucache.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/nucache.html rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.html diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/publishedstatus.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedstatus.controller.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/publishedstatus.controller.js rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedstatus.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/publishedstatus.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedstatus.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/publishedstatus.html rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedstatus.html diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/xmldataintegrityreport.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/xmldataintegrityreport.controller.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/xmldataintegrityreport.controller.js rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/xmldataintegrityreport.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/xmldataintegrityreport.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/xmldataintegrityreport.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/xmldataintegrityreport.html rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/xmldataintegrityreport.html From 4e87abcc1e11d29dab99789a61e4f91a1710af12 Mon Sep 17 00:00:00 2001 From: elitsa Date: Tue, 2 Oct 2018 14:11:48 +0200 Subject: [PATCH 26/49] Update references to the right sections. --- src/Umbraco.Web/Editors/PublishedStatusController.cs | 4 ++-- src/Umbraco.Web/HealthCheck/HealthCheckController.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/PublishedStatusController.cs b/src/Umbraco.Web/Editors/PublishedStatusController.cs index 087b96a241..937d3f3137 100644 --- a/src/Umbraco.Web/Editors/PublishedStatusController.cs +++ b/src/Umbraco.Web/Editors/PublishedStatusController.cs @@ -18,13 +18,13 @@ namespace Umbraco.Web.Editors public string GetPublishedStatusUrl() { if (_publishedSnapshotService is PublishedCache.XmlPublishedCache.PublishedSnapshotService) - return "views/dashboard/developer/xmldataintegrityreport.html"; + return "views/dashboard/settings/xmldataintegrityreport.html"; //if (service is PublishedCache.PublishedNoCache.PublishedSnapshotService) // return "views/dashboard/developer/nocache.html"; if (_publishedSnapshotService is PublishedCache.NuCache.PublishedSnapshotService) - return "views/dashboard/developer/nucache.html"; + return "views/dashboard/settings/nucache.html"; throw new NotSupportedException("Not supported: " + _publishedSnapshotService.GetType().FullName); } diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs index 6412409488..ac64706292 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs +++ b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.HealthCheck /// /// The API controller used to display the health check info and execute any actions /// - [UmbracoApplicationAuthorize(Core.Constants.Applications.Developer)] + [UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)] public class HealthCheckController : UmbracoAuthorizedJsonController { private readonly HealthCheckCollection _checks; From 0e8d4eed63f7ac0f98d936f323acf081f774a56d Mon Sep 17 00:00:00 2001 From: elitsa Date: Tue, 2 Oct 2018 14:12:53 +0200 Subject: [PATCH 27/49] Move dashboards from the Developer's section into Settings one. --- .../DashboardSettings/Dashboard.config | 208 +++++++-------- src/Umbraco.Web.UI/config/Dashboard.config | 245 +++++++++--------- 2 files changed, 224 insertions(+), 229 deletions(-) diff --git a/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config b/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config index 37f2723c81..4040412603 100644 --- a/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config +++ b/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config @@ -1,114 +1,114 @@  -
- - settings - - - - views/dashboard/settings/settingsdashboardintro.html - - - views/dashboard/settings/settingsdashboardvideos.html - - -
+
+ + settings + + + + views/dashboard/settings/settingsdashboardintro.html + + + views/dashboard/settings/settingsdashboardvideos.html + + + + dashboard/ExamineManagement.ascx + +
-
- - developer - - - - views/dashboard/developer/developerdashboardintro.html - - - views/dashboard/developer/developerdashboardvideos.html - - - - dashboard/ExamineManagement.ascx - -
+
+ + developer + + + + views/dashboard/developer/developerdashboardintro.html + + + views/dashboard/developer/developerdashboardvideos.html + + +
-
- - media - - - - views/dashboard/media/mediafolderbrowser.html - - - - - admin - - - views/dashboard/media/mediadashboardintro.html - - - views/dashboard/media/desktopmediauploader.html - - - views/dashboard/media/mediadashboardvideos.html - - -
+
+ + media + + + + views/dashboard/media/mediafolderbrowser.html + + + + + admin + + + views/dashboard/media/mediadashboardintro.html + + + views/dashboard/media/desktopmediauploader.html + + + views/dashboard/media/mediadashboardvideos.html + + +
-
- - translator - hello - world - - - content - - - - admin - - - views/dashboard/default/startupdashboardintro.html - - - views/dashboard/default/startupdashboardkits.html +
- editor - writer + translator + hello + world - - - views/dashboard/default/startupdashboardvideos.html - - - - dashboard/latestEdits.ascx - - - - views/dashboard/changepassword.html - - -
+ + content + + + + admin + + + views/dashboard/default/startupdashboardintro.html + + + views/dashboard/default/startupdashboardkits.html + + editor + writer + + + + views/dashboard/default/startupdashboardvideos.html + + + + dashboard/latestEdits.ascx + + + + views/dashboard/changepassword.html + + +
-
- - default - member - - - - views/dashboard/members/membersdashboardintro.html - - - members/membersearch.ascx - - - views/dashboard/members/membersdashboardvideos.html - - -
+
+ + default + member + + + + views/dashboard/members/membersdashboardintro.html + + + members/membersearch.ascx + + + views/dashboard/members/membersdashboardvideos.html + + +
diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index afb83ae7f3..857b3fd7bd 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -1,127 +1,122 @@  -
- - settings - - - - views/dashboard/settings/settingsdashboardintro.html - - -
-
- - forms - - - - views/dashboard/forms/formsdashboardintro.html - - -
-
- - developer - - - - views/dashboard/developer/developerdashboardvideos.html - - - - - views/dashboard/developer/examinemanagement.html - - - - - views/dashboard/developer/publishedstatus.html - - -
-
- - media - - - - views/dashboard/media/mediafolderbrowser.html - - -
-
- - forms - - - - views/dashboard/forms/formsdashboardintro.html - - -
-
- - translator - - - content - - - - admin - - - views/dashboard/default/startupdashboardintro.html - - -
-
- - member - - - - views/dashboard/members/membersdashboardvideos.html - - -
-
- - contour - - - plugins/umbracocontour/formsdashboard.ascx - -
-
- - developer - - - - views/dashboard/developer/healthcheck.html - - -
-
- - content - - - - views/dashboard/developer/redirecturls.html - - -
-
- - developer - - - - /App_Plugins/ModelsBuilder/modelsbuilder.htm - - -
-
\ No newline at end of file +
+ + settings + + + + views/dashboard/settings/settingsdashboardintro.html + + + + + views/dashboard/settings/developerdashboardvideos.html + + + + + views/dashboard/settings/examinemanagement.html + + + + + views/dashboard/settings/publishedstatus.html + + +
+
+ + forms + + + + views/dashboard/forms/formsdashboardintro.html + + +
+
+ + media + + + + views/dashboard/media/mediafolderbrowser.html + + +
+
+ + forms + + + + views/dashboard/forms/formsdashboardintro.html + + +
+
+ + translator + + + content + + + + admin + + + views/dashboard/default/startupdashboardintro.html + + +
+
+ + member + + + + views/dashboard/members/membersdashboardvideos.html + + +
+
+ + contour + + + plugins/umbracocontour/formsdashboard.ascx + +
+
+ + settings + + + + views/dashboard/settings/healthcheck.html + + +
+
+ + content + + + + views/dashboard/developer/redirecturls.html + + +
+
+ + settings + + + + /App_Plugins/ModelsBuilder/modelsbuilder.htm + + +
+ From 0ec789ca06cba9ccd3011c7872b5b41be6f29aaf Mon Sep 17 00:00:00 2001 From: elitsa Date: Tue, 2 Oct 2018 14:14:25 +0200 Subject: [PATCH 28/49] Enable PackagesTreeController while disabling the custom UI for the Packages dashboard. --- .../Trees/PackagesTreeController.cs | 180 +++++++++--------- 1 file changed, 89 insertions(+), 91 deletions(-) diff --git a/src/Umbraco.Web/Trees/PackagesTreeController.cs b/src/Umbraco.Web/Trees/PackagesTreeController.cs index 31283acebf..ab47d21803 100644 --- a/src/Umbraco.Web/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web/Trees/PackagesTreeController.cs @@ -1,107 +1,105 @@ -//using System; -//using System.Linq; -//using System.Net.Http.Formatting; -//using Umbraco.Web.Models.Trees; -//using Umbraco.Web.Mvc; -//using Umbraco.Web.WebApi.Filters; -//using umbraco; -//using umbraco.cms.businesslogic.packager; -//using Umbraco.Core.Services; -//using Umbraco.Web._Legacy.Actions; -//using Constants = Umbraco.Core.Constants; +using System; +using System.Linq; +using System.Net.Http.Formatting; +using Umbraco.Web.Models.Trees; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi.Filters; +using umbraco; +using umbraco.cms.businesslogic.packager; +using Umbraco.Core.Services; +using Umbraco.Web._Legacy.Actions; +using Constants = Umbraco.Core.Constants; -//namespace Umbraco.Web.Trees -//{ -// [UmbracoTreeAuthorize(Constants.Trees.Packages)] -// [Tree(Constants.Applications.Developer, Constants.Trees.Packages, null, sortOrder: 0)] -// [PluginController("UmbracoTrees")] -// [CoreTree] -// public class PackagesTreeController : TreeController -// { -// /// -// /// Helper method to create a root model for a tree -// /// -// /// -// protected override TreeNode CreateRootNode(FormDataCollection queryStrings) -// { -// var root = base.CreateRootNode(queryStrings); +namespace Umbraco.Web.Trees +{ + [UmbracoTreeAuthorize(Constants.Trees.Packages)] + [Tree(Constants.Applications.Developer, Constants.Trees.Packages, null, sortOrder: 0)] + [PluginController("UmbracoTrees")] + [CoreTree] + public class PackagesTreeController : TreeController + { + /// + /// Helper method to create a root model for a tree + /// + /// + protected override TreeNode CreateRootNode(FormDataCollection queryStrings) + { + var root = base.CreateRootNode(queryStrings); -// //this will load in a custom UI instead of the dashboard for the root node -// root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Developer, Constants.Trees.Packages, "overview"); -// root.Icon = "icon-box"; + root.Icon = "icon-box"; -// return root; -// } -// protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) -// { -// var nodes = new TreeNodeCollection(); + return root; + } + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) + { + var nodes = new TreeNodeCollection(); -// var createdPackages = CreatedPackage.GetAllCreatedPackages(); + var createdPackages = CreatedPackage.GetAllCreatedPackages(); -// if (id == "created") -// { -// nodes.AddRange( -// createdPackages -// .OrderBy(entity => entity.Data.Name) -// .Select(dt => -// { -// var node = CreateTreeNode(dt.Data.Id.ToString(), id, queryStrings, dt.Data.Name, "icon-inbox", false, -// string.Format("/{0}/framed/{1}", -// queryStrings.GetValue("application"), -// Uri.EscapeDataString("developer/Packages/EditPackage.aspx?id=" + dt.Data.Id))); -// return node; -// })); -// } -// else -// { -// //must be root -// var node = CreateTreeNode( -// "created", -// id, -// queryStrings, -// Services.TextService.Localize("treeHeaders/createdPackages"), -// "icon-folder", -// createdPackages.Count > 0, -// string.Empty); + if (id == "created") + { + nodes.AddRange( + createdPackages + .OrderBy(entity => entity.Data.Name) + .Select(dt => + { + var node = CreateTreeNode(dt.Data.Id.ToString(), id, queryStrings, dt.Data.Name, "icon-inbox", false, + string.Format("/{0}/framed/{1}", + queryStrings.GetValue("application"), + Uri.EscapeDataString("developer/Packages/EditPackage.aspx?id=" + dt.Data.Id))); + return node; + })); + } + else + { + //must be root + var node = CreateTreeNode( + "created", + id, + queryStrings, + Services.TextService.Localize("treeHeaders/createdPackages"), + "icon-folder", + createdPackages.Count > 0, + string.Empty); -// //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. -// node.AdditionalData["jsClickCallback"] = "javascript:void(0);"; + //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. + node.AdditionalData["jsClickCallback"] = "javascript:void(0);"; -// nodes.Add(node); -// } + nodes.Add(node); + } -// return nodes; -// } + return nodes; + } -// protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) -// { -// var menu = new MenuItemCollection(); + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + var menu = new MenuItemCollection(); -// // Root actions -// if (id == "-1") -// { -// menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))) -// .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); -// } -// else if (id == "created") -// { -// menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))) -// .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); + // Root actions + if (id == "-1") + { + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))) + .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); + } + else if (id == "created") + { + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))) + .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); -// menu.Items.Add( -// Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); -// } -// else -// { -// //it's a package node -// menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); -// } + menu.Items.Add( + Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + } + else + { + //it's a package node + menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); + } -// return menu; -// } -// } -//} + return menu; + } + } +} From cda24b4fba01def0d56a36750199b2eb6468d0a6 Mon Sep 17 00:00:00 2001 From: elitsa Date: Tue, 2 Oct 2018 14:19:00 +0200 Subject: [PATCH 29/49] Updating the right path to the Packages dashboard subviews. Adding new properties and attribute to meet the new implementation standards of a section. --- .../src/views/packages/overview.controller.js | 17 ++++++++++------- .../src/views/packages/overview.html | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js index 369d919b7d..42fcddaa56 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js @@ -27,25 +27,28 @@ { "name": "Packages", "icon": "icon-cloud", - "view": "views/packager/views/repo.html", - "active": !installPackageUri || installPackageUri === "navigation" + "view": "views/packages/views/repo.html", + "active": !installPackageUri || installPackageUri === "navigation", + "alias": "umbPackages" }, { "name": "Installed", "icon": "icon-box", - "view": "views/packager/views/installed.html", - "active": installPackageUri === "installed" + "view": "views/packages/views/installed.html", + "active": installPackageUri === "installed", + "alias": "umbInstalled" }, { "name": "Install local", "icon": "icon-add", - "view": "views/packager/views/install-local.html", - "active": installPackageUri === "local" + "view": "views/packages/views/install-local.html", + "active": installPackageUri === "local", + "alias": "umbInstallLocal" } ]; $timeout(function () { - navigationService.syncTree({ tree: "packager", path: "-1" }); + navigationService.syncTree({ tree: "packages", path: "-1" }); }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/overview.html b/src/Umbraco.Web.UI.Client/src/views/packages/overview.html index 43f08a9882..250dc889d6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/overview.html @@ -16,7 +16,7 @@ + sub-views="vm.page.navigation" model="vm.page"> From ce2cf7caa3cba066e284b554b4ebe04277b21d7a Mon Sep 17 00:00:00 2001 From: elitsa Date: Tue, 2 Oct 2018 14:21:28 +0200 Subject: [PATCH 30/49] Updating the count of classes meeting the condition, due to uncommenting PackagesTreeController.cs --- src/Umbraco.Tests/Composing/TypeFinderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 955f6f94c8..a8624e8871 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -90,7 +90,7 @@ namespace Umbraco.Tests.Composing Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] typesFound = TypeFinder.FindClassesWithAttribute(new[] { typeof (UmbracoContext).Assembly }); - Assert.AreEqual(21, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] + Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] } private static ProfilingLogger GetTestProfilingLogger() From e44ed00c970b70bdb968ba6778b01c6131d8444b Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 3 Oct 2018 10:31:35 +0200 Subject: [PATCH 31/49] Another fallback iteration --- src/Umbraco.Core/Constants-Content.cs | 48 ------- .../Models/PublishedContent/Fallback.cs | 75 +++++++++++ .../IPublishedValueFallback.cs | 74 +++++------ .../NoopPublishedValueFallback.cs | 36 +++++- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- .../Repositories/LanguageRepositoryTest.cs | 12 +- .../PublishedContentLanguageVariantTests.cs | 59 +++++++-- .../PublishedContent/PublishedContentTests.cs | 4 +- .../PublishedValueFallback.cs | 119 +++++++++++------- src/Umbraco.Web/PublishedContentExtensions.cs | 30 +++-- src/Umbraco.Web/PublishedElementExtensions.cs | 29 +++-- src/Umbraco.Web/PublishedPropertyExtension.cs | 63 +++++----- src/Umbraco.Web/umbraco.presentation/item.cs | 4 +- 13 files changed, 341 insertions(+), 214 deletions(-) delete mode 100644 src/Umbraco.Core/Constants-Content.cs create mode 100644 src/Umbraco.Core/Models/PublishedContent/Fallback.cs diff --git a/src/Umbraco.Core/Constants-Content.cs b/src/Umbraco.Core/Constants-Content.cs deleted file mode 100644 index 3f12ece6dc..0000000000 --- a/src/Umbraco.Core/Constants-Content.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Umbraco.Core -{ - public static partial class Constants - { - /// - /// Defines content retrieval related constants - /// - public static class Content - { - /// - /// Defines core supported content fall-back options when retrieving content property values. - /// Defined as constants rather than enum to allow solution or package defined fall-back methods. - /// - public static class ValueFallback - { - /// - /// No fallback at all. - /// - public const int None = -1; - - /// - /// Default fallback. - /// - public const int Default = 0; - - /// - /// Recurse up the tree. - /// - public const int Recurse = 1; - - /// - /// Fallback to other languages. - /// - public const int Language = 2; - - /// - /// Recurse up the tree. If content not found, fallback to other languages. - /// - public const int RecurseThenLanguage = 3; - - /// - /// Fallback to other languages. If content not found, recurse up the tree. - /// - public const int LanguageThenRecurse = 4; - } - } - } -} diff --git a/src/Umbraco.Core/Models/PublishedContent/Fallback.cs b/src/Umbraco.Core/Models/PublishedContent/Fallback.cs new file mode 100644 index 0000000000..0434218555 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/Fallback.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Manages the built-in fallback policies. + /// + public struct Fallback : IEnumerable + { + private readonly int[] _values; + + /// + /// Initializes a new instance of the struct with values. + /// + private Fallback(int[] values) + { + _values = values; + } + + /// + /// Gets an ordered set of fallback policies. + /// + /// + public static Fallback To(params int[] values) => new Fallback(values); + + /// + /// Do not fallback. + /// + public const int None = 0; + + /// + /// Fallback to default value. + /// + public const int DefaultValue = 1; + + /// + /// Gets the fallback to default value policy. + /// + public static Fallback ToDefaultValue => new Fallback(new[] { DefaultValue }); + + /// + /// Fallback to other languages. + /// + public const int Language = 2; + + /// + /// Gets the fallback to language policy. + /// + public static Fallback ToLanguage => new Fallback(new[] { Language }); + + /// + /// Fallback to tree ancestors. + /// + public const int Ancestors = 3; + + /// + /// Gets the fallback to tree ancestors policy. + /// + public static Fallback ToAncestors => new Fallback(new[] { Ancestors }); + + /// + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_values ?? Array.Empty()).GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index c67df36c4f..b03b0515cf 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -7,16 +7,6 @@ { // fixme discussions & challenges // - // - what's with visitedLanguage? should be internal to fallback implementation - // so that should be the case now, with latest changes - // - // - should be as simple as - // model.Value("price", fallback: ValueFallback.Language); - // model.Value("name", fallback: ValueFallback.Recurse); - // - // so chaining things through an array of ints is not... convenient - // it feels like ppl could have ValueFallback.LanguageAndRecurse or something? - // // - the fallback: parameter value must be open, so about anything can be passed to the IPublishedValueFallback // we have it now, it's an integer + constants, cool // @@ -24,14 +14,6 @@ // not! the default value of the fallback: parameter is 'default', not 'none', and if people // want to implement a different default behavior, they have to override the fallback provider // - // - currently, no policies on IPublishedProperty nor IPublishedElement, but some may apply (language) - // todo: implement - // - // - general defaultValue discussion: - // when HasValue is false, the converter may return something, eg an empty enumerable, even though - // defaultValue is null, so should we respect defaultValue only when it is not 'default'? - // todo: when defaultValue==default, and HasValue is false, still return GetValue to ensure this - // // - (and...) // ModelsBuilder model.Value(x => x.Price, ...) extensions need to be adjusted too // @@ -40,14 +22,15 @@ // OTOH we need to implement the readonly thing for languages /// - /// Gets a fallback value for a property. + /// Tries to get a fallback value for a property. /// /// The property. /// The requested culture. /// The requested segment. + /// A fallback strategy. /// An optional default value. - /// Integer value defining method to use for fallback when content not found - /// A fallback value, or null. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. /// /// This method is called whenever property.Value(culture, segment, defaultValue) is called, and /// property.HasValue(culture, segment) is false. @@ -55,18 +38,19 @@ /// At property level, property.GetValue() does *not* implement fallback, and one has to /// get property.Value() or property.Value{T}() to trigger fallback. /// - object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, int fallback); + bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, object defaultValue, out object value); /// - /// Gets a fallback value for a property. + /// Tries to get a fallback value for a property. /// /// The type of the value. /// The property. /// The requested culture. /// The requested segment. + /// A fallback strategy. /// An optional default value. - /// Integer value defining method to use for fallback when content not found - /// A fallback value, or null. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. /// /// This method is called whenever property.Value{T}(culture, segment, defaultValue) is called, and /// property.HasValue(culture, segment) is false. @@ -74,76 +58,78 @@ /// At property level, property.GetValue() does *not* implement fallback, and one has to /// get property.Value() or property.Value{T}() to trigger fallback. /// - T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, int fallback); + bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, T defaultValue, out T value); /// - /// Gets a fallback value for a published element property. + /// Tries to get a fallback value for a published element property. /// /// The published element. /// The property alias. /// The requested culture. /// The requested segment. + /// A fallback strategy. /// An optional default value. - /// Integer value defining method to use for fallback when content not found - /// A fallback value, or null. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. /// /// This method is called whenever getting the property value for the specified alias, culture and /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. /// It can only fallback at element level (no recurse). /// - object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, int fallback); + bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value); /// - /// Gets a fallback value for a published element property. + /// Tries to get a fallback value for a published element property. /// /// The type of the value. /// The published element. /// The property alias. /// The requested culture. /// The requested segment. + /// A fallback strategy. /// An optional default value. - /// Integer value defining method to use for fallback when content not found - /// A fallback value, or null. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. /// /// This method is called whenever getting the property value for the specified alias, culture and /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. /// It can only fallback at element level (no recurse). /// - T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, int fallback); + bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value); /// - /// Gets a fallback value for a published content property. + /// Tries to get a fallback value for a published content property. /// /// The published element. /// The property alias. /// The requested culture. /// The requested segment. + /// A fallback strategy. /// An optional default value. - /// Integer value defining method to use for fallback when content not found - /// A fallback value, or null. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. /// /// This method is called whenever getting the property value for the specified alias, culture and /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. - /// fixme explain & document priority + merge w/recurse? /// - object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback); + bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value); /// - /// Gets a fallback value for a published content property. + /// Tries to get a fallback value for a published content property. /// /// The type of the value. /// The published element. /// The property alias. /// The requested culture. /// The requested segment. + /// A fallback strategy. /// An optional default value. - /// Integer value defining method to use for fallback when content not found - /// A fallback value, or null. + /// The fallback value. + /// A value indicating whether a fallback value could be provided. /// /// This method is called whenever getting the property value for the specified alias, culture and /// segment, either returned no property at all, or a property with HasValue(culture, segment) being false. - /// fixme explain & document priority + merge w/recurse? /// - T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, int fallback); + bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs index d920cefb24..cd7b063d44 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -9,21 +9,45 @@ public class NoopPublishedValueFallback : IPublishedValueFallback { /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, int fallback) => defaultValue; + public bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, object defaultValue, out object value) + { + value = default; + return false; + } /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, int fallback) => defaultValue; + public bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, T defaultValue, out T value) + { + value = default; + return false; + } /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, int fallback) => defaultValue; + public bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value) + { + value = default; + return false; + } /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, int fallback) => defaultValue; + public bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value) + { + value = default; + return false; + } /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback) => defaultValue; + public bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value) + { + value = default; + return false; + } /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, int fallback) => defaultValue; + public bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value) + { + value = default; + return false; + } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 41c9ea1df9..15c057389f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -299,7 +299,6 @@ - @@ -393,6 +392,7 @@ + diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index c62cf9c0db..a063d2e387 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -328,16 +328,16 @@ namespace Umbraco.Tests.Persistence.Repositories // Add language to delete as a fall-back language to another one var repository = CreateRepository(provider); var languageToFallbackFrom = repository.Get(5); - languageToFallbackFrom.FallbackLanguageId = 1; + languageToFallbackFrom.FallbackLanguageId = 2; // fall back to #2 (something we can delete) repository.Save(languageToFallbackFrom); - // Act - var languageToDelete = repository.Get(1); + // delete #2 + var languageToDelete = repository.Get(2); repository.Delete(languageToDelete); - var exists = repository.Exists(1); + var exists = repository.Exists(2); - // Assert + // has been deleted Assert.That(exists, Is.False); } } @@ -369,6 +369,8 @@ namespace Umbraco.Tests.Persistence.Repositories private void CreateTestData() { + //Id 1 is en-US - when Umbraco is installed + var languageDK = new Language("da-DK") { CultureName = "da-DK" }; ServiceContext.LocalizationService.Save(languageDK);//Id 2 diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index d5e01fd424..4e98aea000 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Reflection; using Moq; using NUnit.Framework; using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Tests.Testing; using Umbraco.Web; @@ -63,6 +65,8 @@ namespace Umbraco.Tests.PublishedContent var props = new[] { factory.CreatePropertyType("prop1", 1), + factory.CreatePropertyType("welcomeText", 1), + factory.CreatePropertyType("welcomeText2", 1), }; var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); @@ -168,7 +172,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - var value = content.Value("welcomeText", "es", fallback: Core.Constants.Content.ValueFallback.Language); + var value = content.Value("welcomeText", "es", fallback: Fallback.ToLanguage); Assert.AreEqual("Welcome", value); } @@ -176,7 +180,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - var value = content.Value("welcomeText", "it", fallback: Core.Constants.Content.ValueFallback.Language); + var value = content.Value("welcomeText", "it", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)); Assert.AreEqual("Welcome", value); } @@ -184,7 +188,7 @@ namespace Umbraco.Tests.PublishedContent public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - var value = content.Value("welcomeText", "no", fallback: Core.Constants.Content.ValueFallback.Language); + var value = content.Value("welcomeText", "no", fallback: Fallback.ToLanguage); Assert.IsNull(value); } @@ -200,7 +204,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_Recursively() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); - var value = content.Value("welcomeText2", fallback: Core.Constants.Content.ValueFallback.Recurse); + var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); Assert.AreEqual("Welcome", value); } @@ -208,7 +212,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_With_Recursive_Priority() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); - var value = content.Value("welcomeText", "nl", fallback: Core.Constants.Content.ValueFallback.RecurseThenLanguage); + var value = content.Value("welcomeText", "nl", fallback: Fallback.To(Fallback.Ancestors, Fallback.Language)); // No Dutch value is directly assigned. Check has fallen back to Dutch value from parent. Assert.AreEqual("Welkom", value); @@ -218,7 +222,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_With_Fallback_Language_Priority() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); - var value = content.Value("welcomeText", "nl", fallback: Core.Constants.Content.ValueFallback.LanguageThenRecurse); + var value = content.Value("welcomeText", "nl", fallback: Fallback.ToLanguage); // No Dutch value is directly assigned. Check has fallen back to English value from language variant. Assert.AreEqual("Welcome", value); @@ -228,7 +232,48 @@ namespace Umbraco.Tests.PublishedContent public void Throws_For_Non_Supported_Fallback() { var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); - Assert.Throws(() => content.Value("welcomeText", "nl", fallback: 999)); + Assert.Throws(() => content.Value("welcomeText", "nl", fallback: Fallback.To(999))); + } + + [Test] + public void Can_Fallback_To_Default_Value() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + + // no Dutch value is assigned, so getting null + var value = content.Value("welcomeText", "nl"); + Assert.IsNull(value); + + // even if we 'just' provide a default value + value = content.Value("welcomeText", "nl", defaultValue: "woop"); + Assert.IsNull(value); + + // but it works with proper fallback settings + value = content.Value("welcomeText", "nl", fallback: Fallback.ToDefaultValue, defaultValue: "woop"); + Assert.AreEqual("woop", value); + } + + [Test] + public void Can_Have_Custom_Default_Value() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + + // hack the value, pretend the converter would return something + var prop = content.GetProperty("welcomeText") as SolidPublishedPropertyWithLanguageVariants; + Assert.IsNotNull(prop); + prop.SetValue("nl", "nope"); // HasValue false but getting value returns this + + // there is an EN value + var value = content.Value("welcomeText", "en-US"); + Assert.AreEqual("Welcome", value); + + // there is no NL value and we get the 'converted' value + value = content.Value("welcomeText", "nl"); + Assert.AreEqual("nope", value); + + // but it works with proper fallback settings + value = content.Value("welcomeText", "nl", fallback: Fallback.ToDefaultValue, defaultValue: "woop"); + Assert.AreEqual("woop", value); } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index e8ea3bc829..14dae46bcb 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -341,8 +341,8 @@ namespace Umbraco.Tests.PublishedContent public void Get_Property_Value_Recursive() { var doc = GetNode(1174); - var rVal = doc.Value("testRecursive", fallback: Constants.Content.ValueFallback.Recurse); - var nullVal = doc.Value("DoNotFindThis", fallback: Constants.Content.ValueFallback.Recurse); + var rVal = doc.Value("testRecursive", fallback: Fallback.ToAncestors); + var nullVal = doc.Value("DoNotFindThis", fallback: Fallback.ToAncestors); Assert.AreEqual("This is the recursive val", rVal); Assert.AreEqual(null, nullVal); } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index bfcd339650..4e9cdaa8f9 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; -using ValueFallback = Umbraco.Core.Constants.Content.ValueFallback; namespace Umbraco.Web.Models.PublishedContent { @@ -24,82 +23,110 @@ namespace Umbraco.Web.Models.PublishedContent } /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, int fallback) + public bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, object defaultValue, out object value) { - return GetValue(property, culture, segment, defaultValue, fallback); + return TryGetValue(property, culture, segment, fallback, defaultValue, out value); } /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, int fallback) + public bool TryGetValue(IPublishedProperty property, string culture, string segment, Fallback fallback, T defaultValue, out T value) { - switch (fallback) + foreach (var f in fallback) { - case ValueFallback.None: - case ValueFallback.Default: - return defaultValue; - case ValueFallback.Language: - return TryGetValueWithLanguageFallback(property, culture, segment, defaultValue, out var value2) ? value2 : defaultValue; - default: - throw NotSupportedFallbackMethod(fallback); + switch (f) + { + case Fallback.None: + continue; + case Fallback.DefaultValue: + value = defaultValue; + return true; + case Fallback.Language: + if (TryGetValueWithLanguageFallback(property, culture, segment, defaultValue, out value)) + return true; + break; + default: + throw NotSupportedFallbackMethod(f, "property"); + } } + + value = defaultValue; + return false; } /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, int fallback) + public bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value) { - return GetValue(content, alias, culture, segment, defaultValue, fallback); + return TryGetValue(content, alias, culture, segment, fallback, defaultValue, out value); } /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, int fallback) + public bool TryGetValue(IPublishedElement content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value) { - switch (fallback) + foreach (var f in fallback) { - case ValueFallback.None: - case ValueFallback.Default: - return defaultValue; - case ValueFallback.Language: - return TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value2) ? value2 : defaultValue; - default: - throw NotSupportedFallbackMethod(fallback); + switch (f) + { + case Fallback.None: + continue; + case Fallback.DefaultValue: + value = defaultValue; + return true; + case Fallback.Language: + if (TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out value)) + return true; + break; + default: + throw NotSupportedFallbackMethod(f, "element"); + } } + + value = defaultValue; + return false; } /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback) + public bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, object defaultValue, out object value) { // is that ok? - return GetValue(content, alias, culture, segment, defaultValue, fallback); + return TryGetValue(content, alias, culture, segment, fallback, defaultValue, out value); } /// - public virtual T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, int fallback) + public virtual bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value) { - switch (fallback) + // note: we don't support "recurse & language" which would walk up the tree, + // looking at languages at each level - should someone need it... they'll have + // to implement it. + + foreach (var f in fallback) { - case ValueFallback.None: - case ValueFallback.Default: - return defaultValue; - case ValueFallback.Recurse: - return TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out var value1) ? value1 : defaultValue; - case ValueFallback.Language: - return TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value2) ? value2 : defaultValue; - case ValueFallback.RecurseThenLanguage: - return TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out var value3) - ? value3 - : TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value4) ? value4 : defaultValue; - case ValueFallback.LanguageThenRecurse: - return TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value5) - ? value5 - : TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out var value6) ? value6 : defaultValue; - default: - throw NotSupportedFallbackMethod(fallback); + switch (f) + { + case Fallback.None: + continue; + case Fallback.DefaultValue: + value = defaultValue; + return true; + case Fallback.Language: + if (TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out value)) + return true; + break; + case Fallback.Ancestors: + if (TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out value)) + return true; + break; + default: + throw NotSupportedFallbackMethod(f, "content"); + } } + + value = defaultValue; + return false; } - private NotSupportedException NotSupportedFallbackMethod(int fallback) + private NotSupportedException NotSupportedFallbackMethod(int fallback, string level) { - return new NotSupportedException($"Fallback {GetType().Name} does not support policy code '{fallback}'."); + return new NotSupportedException($"Fallback {GetType().Name} does not support fallback code '{fallback}' at {level} level."); } // tries to get a value, recursing the tree diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 88bb80f604..f0ddf62074 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -184,7 +184,7 @@ namespace Umbraco.Web #endregion #region Value - + /// /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value. /// @@ -192,23 +192,30 @@ namespace Umbraco.Web /// The property alias. /// The variation language. /// The variation segment. - /// The default value. /// Optional fallback strategy. + /// The default value. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. - public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, int fallback = 0) + public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) { var property = content.GetProperty(alias); + // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback); + // else let fallback try to get a value + if (PublishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property?.GetValue(culture, segment); } #endregion #region Value - + /// /// Gets the value of a content's property identified by its alias, converted to a specified type. /// @@ -217,17 +224,24 @@ namespace Umbraco.Web /// The property alias. /// The variation language. /// The variation segment. - /// The default value. /// Optional fallback strategy. + /// The default value. /// The value of the content's property identified by the alias, converted to the specified type. - public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, int fallback = 0) + public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) { var property = content.GetProperty(alias); + // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback); + // else let fallback try to get a value + if (PublishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property == null ? default : property.Value(culture, segment); } // fixme - .Value() refactoring - in progress diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index eea92f4c6c..cd6ede9a7c 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -99,8 +99,8 @@ namespace Umbraco.Web /// The property alias. /// The variation language. /// The variation segment. - /// The default value. /// Optional fallback strategy. + /// The default value. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -108,19 +108,21 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default, int fallback = 0) + public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) { var property = content.GetProperty(alias); + // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - // fixme defaultValue is a problem here - // assuming the value may return as an IEnumerable and no defaultValue is provided, then defaultValue is null - // and if HasValue is false, what we get is 'null' - but the converter may instead have been able to return an - // empty enumerable, which would be way nicer - so we need a way to tell that 'no defaultValue has been provided'? + // else let fallback try to get a value + if (PublishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback); + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property?.GetValue(culture, segment); } #endregion @@ -135,8 +137,8 @@ namespace Umbraco.Web /// The property alias. /// The variation language. /// The variation segment. - /// The default value. /// Optional fallback strategy. + /// The default value. /// The value of the content's property identified by the alias, converted to the specified type. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -144,14 +146,21 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default, int fallback = 0) + public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) { var property = content.GetProperty(alias); + // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback); + // else let fallback try to get a value + if (PublishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) + return value; + + // else... if we have a property, at least let the converter return its own + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property == null ? default : property.Value(culture, segment); } #endregion diff --git a/src/Umbraco.Web/PublishedPropertyExtension.cs b/src/Umbraco.Web/PublishedPropertyExtension.cs index 6632076022..b431f24828 100644 --- a/src/Umbraco.Web/PublishedPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedPropertyExtension.cs @@ -16,56 +16,49 @@ namespace Umbraco.Web #region Value - public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default, int fallback = 0) + public static object Value(this IPublishedProperty property, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) { if (property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(property, culture, segment, defaultValue, fallback); + return PublishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var value) + ? value + : property.GetValue(culture, segment); // give converter a chance to return it's own vision of "no value" } #endregion #region Value - public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default, int fallback = 0) + public static T Value(this IPublishedProperty property, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) { - // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below) - // but we have no way to tell whether default value is specified or not - we could do it with overloads, but then defaultValue - // comes right after property and conflicts with culture when T is string - so we're just not doing it - if defaultValue is - // default, whether specified or not, we give a chance to the converter - // - //if (!property.HasValue(culture, segment) && 'defaultValue is explicitely specified') return defaultValue; + if (property.HasValue(culture, segment)) + { + // we have a value + // try to cast or convert it + var value = property.GetValue(culture, segment); + if (value is T valueAsT) return valueAsT; + var valueConverted = value.TryConvertTo(); + if (valueConverted) return valueConverted.Result; - // give the converter a chance to handle the default value differently - // eg for IEnumerable it may return Enumerable.Empty instead of null + // cannot cast nor convert the value, nothing we can return but 'default' + // note: we don't want to fallback in that case - would make little sense + return default; + } - var value = property.GetValue(culture, segment); + // we don't have a value, try fallback + if (PublishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var fallbackValue)) + return fallbackValue; - // if value is null (strange but why not) it still is OK to call TryConvertTo - // because it's an extension method (hence no NullRef) which will return a - // failed attempt. So, no need to care for value being null here. + // we don't have a value - neither direct nor fallback + // give a chance to the converter to return something (eg empty enumerable) + var noValue = property.GetValue(culture, segment); + if (noValue is T noValueAsT) return noValueAsT; + var noValueConverted = noValue.TryConvertTo(); + if (noValueConverted) return noValueConverted.Result; - // if already the requested type, return - if (value is T variable) return variable; - - // if can convert to requested type, return - var convert = value.TryConvertTo(); - if (convert.Success) return convert.Result; - - // at that point, the code tried with the raw value - // that makes no sense because it sort of is unpredictable, - // you wouldn't know when the converters run or don't run. - // so, it's commented out now. - - // try with the raw value - //var source = property.ValueSource; - //if (source is string) source = TextValueConverterHelper.ParseStringValueSource((string)source); - //if (source is T) return (T)source; - //convert = source.TryConvertTo(); - //if (convert.Success) return convert.Result; - - return PublishedValueFallback.GetValue(property, culture, segment, defaultValue, fallback); + // cannot cast noValue nor convert it, nothing we can return but 'default' + return default; } #endregion diff --git a/src/Umbraco.Web/umbraco.presentation/item.cs b/src/Umbraco.Web/umbraco.presentation/item.cs index cca6076391..d15d3d33c1 100644 --- a/src/Umbraco.Web/umbraco.presentation/item.cs +++ b/src/Umbraco.Web/umbraco.presentation/item.cs @@ -82,7 +82,7 @@ namespace umbraco //check for published content and get its value using that if (publishedContent != null && (publishedContent.HasProperty(_fieldName) || recursive)) { - var pval = publishedContent.Value(_fieldName, fallback: Constants.Content.ValueFallback.Recurse); + var pval = publishedContent.Value(_fieldName, fallback: Fallback.ToAncestors); var rval = pval == null ? string.Empty : pval.ToString(); _fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval; } @@ -102,7 +102,7 @@ namespace umbraco { if (publishedContent != null && (publishedContent.HasProperty(altFieldName) || recursive)) { - var pval = publishedContent.Value(altFieldName, fallback: Constants.Content.ValueFallback.Recurse); + var pval = publishedContent.Value(altFieldName, fallback: Fallback.ToAncestors); var rval = pval == null ? string.Empty : pval.ToString(); _fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval; } From 914b8177bd538cb8f583baf9ad1794d64112297c Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 3 Oct 2018 11:11:55 +0200 Subject: [PATCH 32/49] Fix the migration plan --- src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index e28193ac5e..4bb738a6b3 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -130,7 +130,12 @@ namespace Umbraco.Core.Migrations.Upgrade .Chain("{5F4597F4-A4E0-4AFE-90B5-6D2F896830EB}"); // to next // resume at {5F4597F4-A4E0-4AFE-90B5-6D2F896830EB} ... - Chain("{B19BF0F2-E1C6-4AEB-A146-BC559D97A2C6}"); + //Chain("{B19BF0F2-E1C6-4AEB-A146-BC559D97A2C6}"); + Chain("{290C18EE-B3DE-4769-84F1-1F467F3F76DA}"); + From("{B19BF0F2-E1C6-4AEB-A146-BC559D97A2C6}") + .Chain("{290C18EE-B3DE-4769-84F1-1F467F3F76DA}"); + // resume at {290C18EE-B3DE-4769-84F1-1F467F3F76DA}... + //FINAL From f2825a6308cd021dfab12be1caa39322e7854c99 Mon Sep 17 00:00:00 2001 From: elitsa Date: Wed, 3 Oct 2018 14:12:18 +0200 Subject: [PATCH 33/49] Adding back wrongly deleted code. --- src/Umbraco.Web/Trees/PackagesTreeController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Trees/PackagesTreeController.cs b/src/Umbraco.Web/Trees/PackagesTreeController.cs index ab47d21803..235f249509 100644 --- a/src/Umbraco.Web/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web/Trees/PackagesTreeController.cs @@ -25,7 +25,9 @@ namespace Umbraco.Web.Trees protected override TreeNode CreateRootNode(FormDataCollection queryStrings) { var root = base.CreateRootNode(queryStrings); - + + root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Developer, Constants.Trees.Packages, "overview"); + root.Icon = "icon-box"; return root; From 2fca4579e7eb110becebe36ef07365ee202cb580 Mon Sep 17 00:00:00 2001 From: elitsa Date: Wed, 3 Oct 2018 14:16:06 +0200 Subject: [PATCH 34/49] Relocate "Redirect URLs" markup and controller into a newly created 'content' folder. --- .../dashboard/{developer => content}/redirecturls.controller.js | 0 .../src/views/dashboard/{developer => content}/redirecturls.html | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => content}/redirecturls.controller.js (100%) rename src/Umbraco.Web.UI.Client/src/views/dashboard/{developer => content}/redirecturls.html (100%) diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/redirecturls.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/content/redirecturls.controller.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/redirecturls.controller.js rename to src/Umbraco.Web.UI.Client/src/views/dashboard/content/redirecturls.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/redirecturls.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/content/redirecturls.html similarity index 100% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/developer/redirecturls.html rename to src/Umbraco.Web.UI.Client/src/views/dashboard/content/redirecturls.html From 83c66730e2c31a9d3836d229356b5dd962bd88f8 Mon Sep 17 00:00:00 2001 From: elitsa Date: Wed, 3 Oct 2018 14:19:35 +0200 Subject: [PATCH 35/49] Updating tabs into Settings section. --- .../config/Dashboard.Release.config | 31 ++++++++----------- src/Umbraco.Web.UI/config/Dashboard.config | 12 +++---- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI/config/Dashboard.Release.config b/src/Umbraco.Web.UI/config/Dashboard.Release.config index d4ffc41de8..0a39395213 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.Release.config +++ b/src/Umbraco.Web.UI/config/Dashboard.Release.config @@ -10,6 +10,16 @@ views/dashboard/settings/settingsdashboardintro.html + + + views/dashboard/settings/examinemanagement.html + + + + + views/dashboard/settings/publishedstatus.html + +
@@ -27,21 +37,6 @@ developer - - - views/dashboard/developer/developerdashboardvideos.html - - - - - views/dashboard/developer/examinemanagement.html - - - - - views/dashboard/developer/publishedstatus.html - -
@@ -99,11 +94,11 @@
- developer + settings - views/dashboard/developer/healthcheck.html + views/dashboard/settings/healthcheck.html
@@ -113,7 +108,7 @@ - views/dashboard/developer/redirecturls.html + views/dashboard/content/redirecturls.html
diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index 857b3fd7bd..c1a1d8815a 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -9,11 +9,6 @@ views/dashboard/settings/settingsdashboardintro.html - - - views/dashboard/settings/developerdashboardvideos.html - - views/dashboard/settings/examinemanagement.html @@ -35,6 +30,11 @@ +
+ + developer + +
media @@ -105,7 +105,7 @@ - views/dashboard/developer/redirecturls.html + views/dashboard/content/redirecturls.html
From 7468c4ab0442d6866ee468fc0088788fad7652e7 Mon Sep 17 00:00:00 2001 From: elitsa Date: Wed, 3 Oct 2018 14:20:46 +0200 Subject: [PATCH 36/49] Disabling packages section dashboard. --- src/Umbraco.Web/Editors/DashboardHelper.cs | 69 +++++++++++----------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Web/Editors/DashboardHelper.cs b/src/Umbraco.Web/Editors/DashboardHelper.cs index b40f289d04..85f0bc8a28 100644 --- a/src/Umbraco.Web/Editors/DashboardHelper.cs +++ b/src/Umbraco.Web/Editors/DashboardHelper.cs @@ -46,49 +46,52 @@ namespace Umbraco.Web.Editors var tabs = new List>(); var i = 1; - // The dashboard config can contain more than one area inserted by a package. - foreach (var dashboardSection in UmbracoConfig.For.DashboardSettings().Sections.Where(x => x.Areas.Contains(section))) - { - //we need to validate access to this section - if (DashboardSecurity.AuthorizeAccess(dashboardSection, currentUser, _sectionService) == false) - continue; + //disable packages section dashboard + if (section != "packages") - //User is authorized - foreach (var tab in dashboardSection.Tabs) + // The dashboard config can contain more than one area inserted by a package. + foreach (var dashboardSection in UmbracoConfig.For.DashboardSettings().Sections.Where(x => x.Areas.Contains(section))) { - //we need to validate access to this tab - if (DashboardSecurity.AuthorizeAccess(tab, currentUser, _sectionService) == false) + //we need to validate access to this section + if (DashboardSecurity.AuthorizeAccess(dashboardSection, currentUser, _sectionService) == false) continue; - var dashboardControls = new List(); - - foreach (var control in tab.Controls) + //User is authorized + foreach (var tab in dashboardSection.Tabs) { - if (DashboardSecurity.AuthorizeAccess(control, currentUser, _sectionService) == false) + //we need to validate access to this tab + if (DashboardSecurity.AuthorizeAccess(tab, currentUser, _sectionService) == false) continue; - var dashboardControl = new DashboardControl(); - var controlPath = control.ControlPath.Trim(); - dashboardControl.Caption = control.PanelCaption; - dashboardControl.Path = IOHelper.FindFile(controlPath); - if (controlPath.ToLowerInvariant().EndsWith(".ascx".ToLowerInvariant())) - dashboardControl.ServerSide = true; + var dashboardControls = new List(); - dashboardControls.Add(dashboardControl); + foreach (var control in tab.Controls) + { + if (DashboardSecurity.AuthorizeAccess(control, currentUser, _sectionService) == false) + continue; + + var dashboardControl = new DashboardControl(); + var controlPath = control.ControlPath.Trim(); + dashboardControl.Caption = control.PanelCaption; + dashboardControl.Path = IOHelper.FindFile(controlPath); + if (controlPath.ToLowerInvariant().EndsWith(".ascx".ToLowerInvariant())) + dashboardControl.ServerSide = true; + + dashboardControls.Add(dashboardControl); + } + + tabs.Add(new Tab + { + Id = i, + Alias = tab.Caption.ToSafeAlias(), + IsActive = i == 1, + Label = tab.Caption, + Properties = dashboardControls + }); + + i++; } - - tabs.Add(new Tab - { - Id = i, - Alias = tab.Caption.ToSafeAlias(), - IsActive = i == 1, - Label = tab.Caption, - Properties = dashboardControls - }); - - i++; } - } //In case there are no tabs or a user doesn't have access the empty tabs list is returned return tabs; From f3d1cc29bb5a80bb6c668b58f473fbc7e24b714c Mon Sep 17 00:00:00 2001 From: elitsa Date: Wed, 3 Oct 2018 14:21:41 +0200 Subject: [PATCH 37/49] Remove file related to old Developer's dashboard. --- .../settings/developerdashboardvideos.html | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/views/dashboard/settings/developerdashboardvideos.html diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/developerdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/developerdashboardvideos.html deleted file mode 100644 index 1e72968f46..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/developerdashboardvideos.html +++ /dev/null @@ -1,21 +0,0 @@ - - - -

Hours of Umbraco training videos are only a click away

-

Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco, then visit umbraco.tv for even more Umbraco videos.

-
-
- - From 61a926e7e627ffff5ba5b8df724d2ff82fe36697 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 3 Oct 2018 14:43:32 +0200 Subject: [PATCH 38/49] Cleanup npm package[-log].json files --- src/Umbraco.Web.UI.Client/package-lock.json | 4979 +++++++++++++++---- src/Umbraco.Web.UI.Client/package.json | 4 +- 2 files changed, 3909 insertions(+), 1074 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 51550250d3..43a973b4f1 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -2,10 +2,67 @@ "requires": true, "lockfileVersion": 1, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, "accepts": { @@ -21,7 +78,7 @@ "accord": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/accord/-/accord-0.28.0.tgz", - "integrity": "sha1-vsUWovci59UPX59C+Bt387lUSLo=", + "integrity": "sha512-sPF34gqHegaCSryKf5wHJ8wREK1dTZnHmC9hsB7D8xjntRdd30DXDPKf0YVIcSvnXJmcYu5SCvZRz28H++kFhQ==", "dev": true, "requires": { "convert-source-map": "^1.5.0", @@ -41,17 +98,17 @@ }, "dependencies": { "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha1-3Eu8emyp2Rbe5dQ1FvAJK1j3uKs=", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true } } }, "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", "dev": true }, "acorn-jsx": { @@ -86,13 +143,15 @@ } }, "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", + "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", "dev": true, "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "ajv-keywords": { @@ -151,19 +210,19 @@ }, "angular": { "version": "1.3.20", - "resolved": "https://registry.npmjs.org/angular/-/angular-1.3.20.tgz", + "resolved": "http://registry.npmjs.org/angular/-/angular-1.3.20.tgz", "integrity": "sha1-sjo9fF5/mffZW5tNSbmsNVJpuu4=", "dev": true }, "angular-animate": { "version": "1.3.20", - "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.3.20.tgz", + "resolved": "http://registry.npmjs.org/angular-animate/-/angular-animate-1.3.20.tgz", "integrity": "sha1-0XB8cn+K0N8hxKLewgzX/ElFtSo=", "dev": true }, "ansi-colors": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { @@ -224,7 +283,7 @@ "anymatch": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { "micromatch": "^2.1.5", @@ -348,7 +407,7 @@ "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "~1.0.2" @@ -363,7 +422,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "arr-union": { @@ -393,7 +452,7 @@ "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true }, "array-union": { @@ -437,10 +496,13 @@ "optional": true }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } }, "assert-plus": { "version": "0.2.0", @@ -462,12 +524,12 @@ "optional": true }, "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha1-YaKau2/MAm/qd+VtHG7FOnlZUfQ=", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "dev": true, "requires": { - "lodash": "^4.14.0" + "lodash": "^4.17.10" } }, "async-each": { @@ -496,9 +558,9 @@ "dev": true }, "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "autoprefixer": { @@ -522,14 +584,14 @@ "dev": true }, "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha1-1NDpudv8p3vwjusKikcVUP454ok=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, "axios": { "version": "0.15.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.15.3.tgz", "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=", "dev": true, "optional": true, @@ -539,7 +601,7 @@ "dependencies": { "follow-redirects": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=", "dev": true, "optional": true, @@ -549,17 +611,6 @@ } } }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -575,7 +626,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { "cache-base": "^1.0.1", @@ -599,7 +650,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -608,7 +659,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -617,7 +668,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -647,7 +698,7 @@ }, "basic-auth": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.0.4.tgz", + "resolved": "http://registry.npmjs.org/basic-auth/-/basic-auth-1.0.4.tgz", "integrity": "sha1-Awk1sB3nyblKgksp8/zLdQ06UpA=", "dev": true }, @@ -664,9 +715,9 @@ "dev": true }, "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, "optional": true, "requires": { @@ -717,7 +768,7 @@ }, "uuid": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", "dev": true, "optional": true @@ -759,7 +810,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -782,9 +833,9 @@ } }, "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", + "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", "dev": true }, "bitsyntax": { @@ -800,7 +851,7 @@ "bl": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha1-oWCRFxcQPAdBDO9j71Gzl8Alr5w=", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "dev": true, "requires": { "readable-stream": "^2.3.5", @@ -815,8 +866,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -831,7 +882,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -846,9 +897,9 @@ "dev": true }, "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha1-2VUfnemPH82h5oPRfukaBgLuLrk=", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", + "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", "dev": true }, "body-parser": { @@ -871,7 +922,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -926,7 +977,7 @@ "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -936,7 +987,7 @@ "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { "arr-flatten": "^1.1.0", @@ -975,7 +1026,7 @@ "buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha1-iQ3ZDZI6hz4I4Q5f1RpX5bfM4Ow=", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", "dev": true, "requires": { "buffer-alloc-unsafe": "^1.1.0", @@ -985,7 +1036,13 @@ "buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha1-vX3CauKXLQ7aJTvgYdupkjScGfA=", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", "dev": true }, "buffer-fill": { @@ -995,11 +1052,10 @@ "dev": true }, "buffer-from": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", - "integrity": "sha1-FfS5vO8BIETfMRQsFDM8r24CYNA=", - "dev": true, - "optional": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true }, "buffer-more-ints": { "version": "0.0.2", @@ -1033,8 +1089,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -1049,7 +1105,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -1057,7 +1113,7 @@ }, "uuid": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", "dev": true }, @@ -1097,6 +1153,15 @@ "nodemailer-fetch": "1.6.0", "nodemailer-shared": "1.1.0", "punycode": "1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true, + "optional": true + } } }, "builtin-modules": { @@ -1114,7 +1179,7 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { "collection-visit": "^1.0.0", @@ -1150,9 +1215,9 @@ "dev": true }, "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", "dev": true }, "camelcase-keys": { @@ -1163,14 +1228,6 @@ "requires": { "camelcase": "^2.0.0", "map-obj": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - } } }, "caniuse-api": { @@ -1186,9 +1243,9 @@ } }, "caniuse-db": { - "version": "1.0.30000836", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000836.tgz", - "integrity": "sha1-eItsj28CmRdDsYzbvVT5bQW0uVo=", + "version": "1.0.30000889", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000889.tgz", + "integrity": "sha512-Rf9Sbm2KS7s6Rk8iNeI5zJdquqctXBXAfy/bb1tCCYRds5RAaHNdyt2D4z8TSRToDkYsAwiSBV/bFHR+4IgTiw==", "dev": true }, "canonical-path": { @@ -1198,9 +1255,9 @@ "dev": true }, "capture-stack-trace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", "dev": true }, "caseless": { @@ -1226,12 +1283,6 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", "dev": true - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true } } }, @@ -1247,7 +1298,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -1267,9 +1318,9 @@ } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "chokidar": { @@ -1324,7 +1375,7 @@ "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=", + "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", "dev": true, "requires": { "chalk": "^1.1.3" @@ -1333,7 +1384,7 @@ "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -1377,6 +1428,14 @@ "center-align": "^0.1.1", "right-align": "^0.1.1", "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + } } }, "clone": { @@ -1400,7 +1459,7 @@ "cloneable-readable": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", - "integrity": "sha1-1ZHe5Kj4vBXaQ86X3O66E9Q+KmU=", + "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -1416,8 +1475,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -1432,7 +1491,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -1441,9 +1500,9 @@ } }, "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", + "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", "dev": true }, "coa": { @@ -1477,12 +1536,12 @@ } }, "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha1-wSYRB66y8pTr/+ye2eytUppgl+0=", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -1503,7 +1562,7 @@ "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha1-k4NDeaHMmgxh+C9S8NBDIiUb1aI=", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, "colormin": { @@ -1518,9 +1577,9 @@ } }, "colors": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.4.tgz", - "integrity": "sha1-4MtB0+SyCAazv8J/RVnwG5S8L3w=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz", + "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ==", "dev": true }, "combine-lists": { @@ -1533,9 +1592,9 @@ } }, "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -1543,7 +1602,7 @@ }, "commander": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, "requires": { @@ -1569,12 +1628,12 @@ "dev": true }, "compressible": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", - "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=", + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz", + "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==", "dev": true, "requires": { - "mime-db": ">= 1.33.0 < 2" + "mime-db": ">= 1.36.0 < 2" } }, "compression": { @@ -1593,7 +1652,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -1615,11 +1674,12 @@ "dev": true }, "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "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" @@ -1633,8 +1693,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -1649,7 +1709,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -1660,7 +1720,7 @@ "concat-with-sourcemaps": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", - "integrity": "sha1-1OqT8FriV5CVG5nns7CeOQikCC4=", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", "dev": true, "requires": { "source-map": "^0.6.1" @@ -1669,7 +1729,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -1715,7 +1775,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -1750,7 +1810,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -1784,14 +1844,17 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } }, "cookie": { "version": "0.1.3", @@ -1836,7 +1899,7 @@ "cosmiconfig": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha1-YXPOvVb6wELB9DkO33r2wHx8uJI=", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", "dev": true, "requires": { "is-directory": "^0.3.1", @@ -1850,7 +1913,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -1872,27 +1935,23 @@ } }, "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, - "optional": true, "requires": { - "lru-cache": "^4.0.1", + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" }, "dependencies": { - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha1-oRdc80lt/IQ2wVbDNLSVWZK85pw=", - "dev": true, - "optional": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true } } }, @@ -1923,15 +1982,15 @@ "dev": true }, "css-select": { - "version": "1.3.0-rc0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.3.0-rc0.tgz", - "integrity": "sha1-b5MZaqrnN2ZuoQNqjLFKj8t6kjE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.0.tgz", + "integrity": "sha512-MGhoq1S9EyPgZIGnts8Yz5WwUOyHmPMdlqeifsYs/xFX7AAm3hY0RJe1dqVlXtYPI66Nsk39R/sa5/ree6L2qg==", "dev": true, "optional": true, "requires": { "boolbase": "^1.0.0", "css-what": "2.1", - "domutils": "1.5.1", + "domutils": "^1.7.0", "nth-check": "^1.0.1" } }, @@ -1943,13 +2002,13 @@ "optional": true }, "css-tree": { - "version": "1.0.0-alpha25", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha25.tgz", - "integrity": "sha1-G7+r+/bu708B2RCP8u3Qvi/jVZc=", + "version": "1.0.0-alpha.28", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz", + "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==", "dev": true, "optional": true, "requires": { - "mdn-data": "^1.0.0", + "mdn-data": "~1.1.0", "source-map": "^0.5.3" } }, @@ -1969,7 +2028,7 @@ }, "cssnano": { "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "resolved": "http://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", "dev": true, "requires": { @@ -2083,7 +2142,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -2199,7 +2258,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2296,8 +2355,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -2312,7 +2371,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -2327,16 +2386,6 @@ "is-utf8": "^0.2.0" } }, - "strip-bom-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", - "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", - "dev": true, - "requires": { - "first-chunk-stream": "^1.0.0", - "strip-bom": "^2.0.0" - } - }, "unique-stream": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", @@ -2413,7 +2462,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2474,7 +2523,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2534,7 +2583,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2597,7 +2646,7 @@ "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha1-xPp8lUBKF6nD6Mp+FTcxK3NjMKw=", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, "deep-is": { @@ -2616,19 +2665,18 @@ } }, "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" + "object-keys": "^1.0.12" } }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { "is-descriptor": "^1.0.2", @@ -2638,7 +2686,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -2647,7 +2695,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -2656,7 +2704,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -2706,22 +2754,6 @@ "pify": "^2.0.0", "pinkie-promise": "^2.0.0", "rimraf": "^2.2.8" - }, - "dependencies": { - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } } }, "delayed-stream": { @@ -2809,9 +2841,9 @@ "optional": true }, "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "dev": true, "optional": true, "requires": { @@ -2930,7 +2962,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -3027,8 +3059,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3043,7 +3075,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3058,16 +3090,6 @@ "is-utf8": "^0.2.0" } }, - "strip-bom-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", - "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", - "dev": true, - "requires": { - "first-chunk-stream": "^1.0.0", - "strip-bom": "^2.0.0" - } - }, "unique-stream": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", @@ -3118,7 +3140,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -3134,7 +3156,7 @@ "duplexify": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha1-WSkD9dgLONA3IgVBJk1poZj7NBA=", + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -3146,7 +3168,7 @@ "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -3160,8 +3182,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3176,7 +3198,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3192,16 +3214,25 @@ "requires": { "onetime": "^1.0.0", "set-immediate-shim": "^1.0.0" + }, + "dependencies": { + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + } } }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "ee-first": { @@ -3211,9 +3242,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.45", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.45.tgz", - "integrity": "sha1-RYrBscXHYM6IEaFtK/vZfsMLr7g=", + "version": "1.3.73", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.73.tgz", + "integrity": "sha512-6PIg7v9zRoVGh6EheRF8h6Plti+3Yo/qtHobS4/Htyt53DNHmKKGFqSae1AIk0k1S4gCQvt7I2WgpbuZNcDY+g==", "dev": true }, "encodeurl": { @@ -3349,7 +3380,7 @@ "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "optional": true, "requires": { @@ -3357,9 +3388,9 @@ } }, "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { "is-arrayish": "^0.2.1" @@ -3396,7 +3427,7 @@ "es-abstract": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha1-nbvdJ8aFbwABQhyhh4LXhr+KYWU=", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { "es-to-primitive": "^1.1.1", @@ -3407,19 +3438,19 @@ } }, "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { - "is-callable": "^1.1.1", + "is-callable": "^1.1.4", "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-symbol": "^1.0.2" } }, "es6-promise": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", "dev": true }, @@ -3433,9 +3464,9 @@ }, "dependencies": { "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", "dev": true } } @@ -3453,9 +3484,9 @@ "dev": true }, "escodegen": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", - "integrity": "sha1-264X75bI5L7bE1b0UE+kzC98t+I=", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", + "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "dev": true, "requires": { "esprima": "^3.1.3", @@ -3474,23 +3505,23 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true } } }, "eslint": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz", - "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.6.1.tgz", + "integrity": "sha512-hgrDtGWz368b7Wqf+v1Z69O3ZebNR0+GA7PtDdbmuz4rInFVUV9uw7whjZEiWyLzCjVb5Rs5WRN1TAS6eo7AYA==", "dev": true, "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", + "@babel/code-frame": "^7.0.0", + "ajv": "^6.5.3", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", - "debug": "^3.1.0", + "debug": "^4.0.1", "doctrine": "^2.1.0", "eslint-scope": "^4.0.0", "eslint-utils": "^1.3.1", @@ -3502,11 +3533,11 @@ "functional-red-black-tree": "^1.0.1", "glob": "^7.1.2", "globals": "^11.7.0", - "ignore": "^4.0.2", + "ignore": "^4.0.6", "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", + "inquirer": "^6.1.0", "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", + "js-yaml": "^3.12.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.5", @@ -3519,26 +3550,13 @@ "progress": "^2.0.0", "regexpp": "^2.0.0", "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "string.prototype.matchall": "^2.0.0", + "semver": "^5.5.1", "strip-ansi": "^4.0.0", "strip-json-comments": "^2.0.1", "table": "^4.0.3", "text-table": "^0.2.0" }, "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "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.1" - } - }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -3565,26 +3583,13 @@ "supports-color": "^5.3.0" } }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "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" - } - }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.0.1.tgz", + "integrity": "sha512-K23FHJ/Mt404FSlp6gSZCevIbTMLX0j3fmHhUEhQ3Wq0FMODW3+cUSoLdy1Gx4polAf4t/lphhmHH35BB8cLYw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "esprima": { @@ -3609,16 +3614,16 @@ "esprima": "^4.0.0" } }, - "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true }, "strip-ansi": { @@ -3631,9 +3636,9 @@ } }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -3726,18 +3731,19 @@ "dev": true }, "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.6.tgz", + "integrity": "sha512-dGXNg4F/FgVzlApjzItL+7naHutA3fDqbV/zAZqDDlXTjiMnQmZKu+prImWKszeBM5UQeGvAl3u1wBiKeDh61g==", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "duplexer": "^0.1.1", + "flatmap-stream": "^0.1.0", + "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" } }, "eventemitter3": { @@ -3749,7 +3755,7 @@ "exec-buffer": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", - "integrity": "sha1-sWhtvZBMfPmC5lLB9aebHlVzCCs=", + "integrity": "sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==", "dev": true, "optional": true, "requires": { @@ -3794,6 +3800,31 @@ "p-finally": "^1.0.0", "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "optional": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + } } }, "executable": { @@ -3909,7 +3940,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { "is-number": "^2.1.0", @@ -3982,7 +4013,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -4007,9 +4038,9 @@ } }, "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extend-shallow": { @@ -4025,7 +4056,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -4034,40 +4065,31 @@ } }, "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", "tmp": "^0.0.33" }, "dependencies": { "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } } } }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -4101,7 +4123,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -4110,7 +4132,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -4119,7 +4141,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -4130,24 +4152,33 @@ } }, "extract-zip": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz", - "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", "dev": true, "requires": { - "concat-stream": "1.6.0", + "concat-stream": "1.6.2", "debug": "2.6.9", - "mkdirp": "0.5.0", + "mkdirp": "0.5.1", "yauzl": "2.4.1" }, "dependencies": { - "mkdirp": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", - "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", "dev": true, "requires": { - "minimist": "0.0.8" + "pend": "~1.2.0" + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "~1.0.1" } } } @@ -4197,23 +4228,21 @@ } }, "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, "requires": { "pend": "~1.2.0" } }, "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, - "optional": true, "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "escape-string-regexp": "^1.0.5" } }, "file-entry-cache": { @@ -4287,7 +4316,7 @@ }, "finalhandler": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz", "integrity": "sha1-llpS2ejQXSuFdUhUH7ibU6JJfZs=", "dev": true, "requires": { @@ -4299,7 +4328,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -4406,6 +4435,12 @@ } } }, + "flatmap-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.0.tgz", + "integrity": "sha512-Nlic4ZRYxikqnK5rj3YoxDVKGGtUjcNDUtvQ7XsdGLZmMwdUYnXf10o1zcXtzEZTBgc6GxeRpQxV/Wu3WPIIHA==", + "dev": true + }, "flatten": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", @@ -4413,12 +4448,12 @@ "dev": true }, "follow-redirects": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.2.tgz", - "integrity": "sha512-kssLorP/9acIdpQ2udQVTiCS5LQmdEz9mvdIfDcl1gYX2tPKFADHSyFdvJS040XdFsPzemWtgI3q8mFVCxtX8A==", + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.8.tgz", + "integrity": "sha512-sy1mXPmv7kLAMKW/8XofG7o9T+6gAjzdZK4AJF6ryqQYUa/hnzgiypoeUecZ53x7XiqKNEpNqLtS97MshW2nxg==", "dev": true, "requires": { - "debug": "^3.1.0" + "debug": "=3.1.0" }, "dependencies": { "debug": { @@ -4447,12 +4482,6 @@ "for-in": "^1.0.1" } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -4494,7 +4523,7 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true }, "fs-extra": { @@ -5082,7 +5111,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "functional-red-black-tree": { @@ -5101,11 +5130,14 @@ } }, "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", "dev": true, - "optional": true + "optional": true, + "requires": { + "is-property": "^1.0.2" + } }, "generate-object-property": { "version": "1.2.0", @@ -5134,7 +5166,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true, "optional": true @@ -5163,7 +5195,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -5225,9 +5257,9 @@ } }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -5321,7 +5353,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -5364,7 +5396,7 @@ "global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { "global-prefix": "^1.0.1", @@ -5386,18 +5418,19 @@ } }, "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", "dev": true }, "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { "array-union": "^1.0.1", + "arrify": "^1.0.0", "glob": "^7.0.3", "object-assign": "^4.0.1", "pify": "^2.0.0", @@ -5440,7 +5473,7 @@ }, "lodash": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=", "dev": true }, @@ -5459,7 +5492,7 @@ "glogg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", - "integrity": "sha1-3PdY5EeJzD89MsHzVio2duajSBA=", + "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", "dev": true, "requires": { "sparkles": "^1.0.0" @@ -5467,7 +5500,7 @@ }, "got": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", + "resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz", "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", "dev": true, "requires": { @@ -5505,8 +5538,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5521,7 +5554,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -5567,7 +5600,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -5585,9 +5618,9 @@ }, "dependencies": { "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", "dev": true }, "clone-stats": { @@ -5603,9 +5636,9 @@ "dev": true }, "vinyl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", - "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", "dev": true, "requires": { "clone": "^2.1.1", @@ -5620,7 +5653,7 @@ }, "gulp-connect": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.0.0.tgz", + "resolved": "http://registry.npmjs.org/gulp-connect/-/gulp-connect-5.0.0.tgz", "integrity": "sha1-8v3zBq6RFGg2jCKF8teC8T7dr04=", "dev": true, "requires": { @@ -5651,8 +5684,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5667,7 +5700,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -5684,26 +5717,12 @@ "eslint": "^5.0.1", "fancy-log": "^1.3.2", "plugin-error": "^1.0.1" - }, - "dependencies": { - "plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "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" - } - } } }, "gulp-imagemin": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/gulp-imagemin/-/gulp-imagemin-4.1.0.tgz", - "integrity": "sha1-XONH8dFwb+08yPF3fKkJSlg7ULc=", + "integrity": "sha512-6nWkrMNY5ub+34+DwlgQdWg21Z4DWAOARLpnyuZ773pGPJrfiyQrkOzdz9DgQSGBQjU1zuw6gd+9clLi6eicuw==", "dev": true, "requires": { "chalk": "^2.1.0", @@ -5722,16 +5741,38 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -5739,16 +5780,44 @@ "supports-color": "^5.3.0" } }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "kind-of": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "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" + } + }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha1-HGszdALCE3YF7+GfEP7DkPb6q1Q=", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -5759,7 +5828,7 @@ "gulp-less": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-3.5.0.tgz", - "integrity": "sha1-gBT0ad38ZUTX3aUAmN7wNxu7T3g=", + "integrity": "sha512-FQLY7unaHdTOXG0jlwxeBQcWoPPrTMQZRA7HfYwSNi9IPVx5l7GJEN72mG4ri2yigp/f/VNGUAJnFMJHBmH3iw==", "dev": true, "requires": { "accord": "^0.28.0", @@ -5771,6 +5840,56 @@ "vinyl-sourcemaps-apply": "^0.2.0" }, "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "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" + } + }, "replace-ext": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", @@ -5801,7 +5920,7 @@ "dependencies": { "ansi-regex": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", "dev": true }, @@ -5813,7 +5932,7 @@ }, "chalk": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", "dev": true, "requires": { @@ -5886,7 +6005,7 @@ }, "lodash": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz", "integrity": "sha1-W3cjA03aTSYuWkb7LFjXzCL3FCA=", "dev": true }, @@ -5976,13 +6095,13 @@ }, "minimist": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=", "dev": true }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -5994,7 +6113,7 @@ }, "strip-ansi": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", "dev": true, "requires": { @@ -6073,7 +6192,7 @@ "gulp-open": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/gulp-open/-/gulp-open-2.1.0.tgz", - "integrity": "sha1-rAHu6JjXenrAgS+tTz1T0IaH1Bw=", + "integrity": "sha512-JzFlG7szoFyjNtlo6f7JnkaNOm0ZaYKofSGvs+4YRI4G+oTknJDn8N14Ocau06yJA1eQoFSOQVHhVWqas1cnHA==", "dev": true, "requires": { "colors": "^1.1.2", @@ -6095,9 +6214,9 @@ } }, "gulp-rename": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz", - "integrity": "sha1-OtRCh2PwXidk3sHGfYaNsnVoeBc=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz", + "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==", "dev": true }, "gulp-sort": { @@ -6178,7 +6297,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -6216,8 +6335,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -6232,7 +6351,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -6326,15 +6445,33 @@ "requires": { "ajv": "^4.9.1", "har-schema": "^1.0.5" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + } } }, "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "^1.0.2" + "function-bind": "^1.1.1" } }, "has-ansi": { @@ -6471,9 +6608,9 @@ } }, "hosted-git-info": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", - "integrity": "sha1-IyNbKasjDFdqqw1PE/wEawsDgiI=", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", "dev": true }, "html-comment-regex": { @@ -6484,7 +6621,7 @@ }, "http-errors": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", "dev": true, "requires": { @@ -6493,9 +6630,9 @@ } }, "http-parser-js": { - "version": "0.4.12", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.12.tgz", - "integrity": "sha1-uc+/Sizybw/DSxDKFImid3HjR08=", + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", + "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", "dev": true }, "http-proxy": { @@ -6568,26 +6705,32 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "iconv-lite": { "version": "0.4.11", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz", + "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz", "integrity": "sha1-LstC/SlHRJIiCaLnxATayHk9it4=", "dev": true }, "ignore": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.3.tgz", - "integrity": "sha512-Z/vAH2GGIEATQnBVXMclE2IGV6i0GyVngKThcGZ5kHgHMxLo9Ow2+XHRq1aEKEej5vOF1TPJNbvX6J/anT0M7A==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, "image-size": { @@ -6611,6 +6754,19 @@ "replace-ext": "^1.0.0" }, "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, "replace-ext": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", @@ -6622,7 +6778,7 @@ "imagemin-gifsicle": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-5.2.0.tgz", - "integrity": "sha1-N4FSTEV2Eu8EkWrzQkGitCv8tAo=", + "integrity": "sha512-K01m5QuPK+0en8oVhiOOAicF7KjrHlCZxS++mfLI2mV/Ksfq/Y9nCXCWDz6jRv13wwlqe5T7hXT+ji2DnLc2yQ==", "dev": true, "optional": true, "requires": { @@ -6658,7 +6814,7 @@ "imagemin-svgo": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/imagemin-svgo/-/imagemin-svgo-6.0.0.tgz", - "integrity": "sha1-LdjIKUa+Qqjiy8rjxb8Ae8K4ueg=", + "integrity": "sha512-xwjBZQKpbkklHtJYnCOwRJjTRJA/nR0hQzKMh+CUZRvm/L0QwKKPJQ9tkPWQHrg+cydPu2i1vLgHuy2E0hKEkg==", "dev": true, "optional": true, "requires": { @@ -6667,10 +6823,17 @@ "svgo": "^1.0.0" }, "dependencies": { + "buffer-from": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", + "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==", + "dev": true, + "optional": true + }, "coa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.1.tgz", - "integrity": "sha1-8/iwsVBz411wJj+xBCyywCPbOK8=", + "integrity": "sha512-5wfTTO8E2/ja4jFSxePXlG5nRu5bBtL/r1HCIpJW/lzT6yDtKl0u0Z4o/Vpz32IpKmBn7HerheEZQgA9N2DarQ==", "dev": true, "optional": true, "requires": { @@ -6687,7 +6850,7 @@ "csso": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", - "integrity": "sha1-e564vmFiiXPBsmHhadLwJACOdYs=", + "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", "dev": true, "optional": true, "requires": { @@ -6697,7 +6860,7 @@ "css-tree": { "version": "1.0.0-alpha.29", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", - "integrity": "sha1-P6nU7zFCy9HDAedmTB81K9gvWjk=", + "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", "dev": true, "optional": true, "requires": { @@ -6710,14 +6873,14 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, "optional": true }, "js-yaml": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", - "integrity": "sha1-LnhEFka9RoLpY/IrbpKCPDCcYtw=", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "optional": true, "requires": { @@ -6726,20 +6889,20 @@ } }, "svgo": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.0.5.tgz", - "integrity": "sha1-cEA2TAYqBTirrP9EAc6momp6OJo=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.1.1.tgz", + "integrity": "sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g==", "dev": true, "optional": true, "requires": { "coa": "~2.0.1", "colors": "~1.1.2", - "css-select": "~1.3.0-rc0", + "css-select": "^2.0.0", "css-select-base-adapter": "~0.1.0", - "css-tree": "1.0.0-alpha25", + "css-tree": "1.0.0-alpha.28", "css-url-regex": "^1.1.0", "csso": "^3.5.0", - "js-yaml": "~3.10.0", + "js-yaml": "^3.12.0", "mkdirp": "~0.5.1", "object.values": "^1.0.4", "sax": "~1.2.4", @@ -6809,25 +6972,25 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", "dev": true, "requires": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.0", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^2.1.0", + "external-editor": "^3.0.0", "figures": "^2.0.0", - "lodash": "^4.3.0", + "lodash": "^4.17.10", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rxjs": "^5.5.2", + "rxjs": "^6.1.0", "string-width": "^2.1.0", "strip-ansi": "^4.0.0", "through": "^2.3.6" @@ -6859,15 +7022,6 @@ "supports-color": "^5.3.0" } }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -6884,9 +7038,9 @@ } }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -6928,7 +7082,7 @@ "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { "is-relative": "^1.0.0", @@ -6979,12 +7133,12 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -7000,7 +7154,7 @@ "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha1-HhrfIZ4e62hNaR+dagX/DTCiTXU=", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, "is-data-descriptor": { @@ -7032,7 +7186,7 @@ "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -7043,7 +7197,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -7133,9 +7287,9 @@ "optional": true }, "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz", + "integrity": "sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==", "dev": true, "optional": true, "requires": { @@ -7174,27 +7328,10 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha1-dkZiRnH9fqVYzNmieVGC8pWPGyQ=", - "dev": true, - "requires": { - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", - "dev": true - } - } - }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -7228,7 +7365,7 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "^3.0.1" @@ -7263,8 +7400,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true, - "optional": true + "dev": true }, "is-redirect": { "version": "1.0.0", @@ -7284,7 +7420,7 @@ "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { "is-unc-path": "^1.0.0" @@ -7318,10 +7454,13 @@ } }, "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } }, "is-tar": { "version": "1.0.0", @@ -7338,7 +7477,7 @@ "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { "unc-path-regex": "^0.1.2" @@ -7347,7 +7486,7 @@ "is-url": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha1-BKTfRtKMTP89c9Af8Gq+sxihqlI=", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", "dev": true }, "is-utf8": { @@ -7365,7 +7504,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "is-zip": { @@ -7426,15 +7565,15 @@ } }, "js-base64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", - "integrity": "sha1-LlRewrDylX9BNWUQIFIU6Y+tZYI=", + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz", + "integrity": "sha512-xcinL3AuDJk7VSzsHgb9DvvIXayBbadtMZ4HFPx8rUszbW1MuNMlwYVC4zzCZ6e1sqZpnNS5ZFYOhXqA39T7LQ==", "dev": true }, "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { @@ -7489,7 +7628,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -7677,7 +7816,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -7768,7 +7907,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, "klaw": { @@ -7819,8 +7958,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -7835,7 +7974,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -7846,7 +7985,7 @@ "less": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", - "integrity": "sha1-zBJg9RyQCp7A2R+2mYE54CUHtjs=", + "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", "dev": true, "requires": { "errno": "^0.1.1", @@ -7897,7 +8036,7 @@ "dependencies": { "iconv-lite": { "version": "0.4.15", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", + "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", "dev": true } @@ -7928,12 +8067,12 @@ "livereload-js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.3.0.tgz", - "integrity": "sha1-w6si6Kr1vzUF2A0JjLrWdyZUjJo=", + "integrity": "sha512-j1R0/FeGa64Y+NmqfZhyoVRzcFlOZ8sNlKzHjh4VvLULFACZhn68XrX5DFg2FhMvSMJmROuFxRSa560ECWKBMg==", "dev": true }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -7962,9 +8101,9 @@ } }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha1-G3eTz3JZ6jj7NmHU04syYK+K5Oc=", + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, "lodash._basecopy": { @@ -8166,7 +8305,7 @@ "lodash.merge": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", - "integrity": "sha1-rcJdnLmbk5HFliTzefu6YNcRHVQ=", + "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==", "dev": true }, "lodash.partialright": { @@ -8264,24 +8403,30 @@ }, "dependencies": { "circular-json": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.5.tgz", - "integrity": "sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.7.tgz", + "integrity": "sha512-/pXoV1JA847qRKPrHbBK6YIBGFF8GOP4wzSgUOA7q0ew0vAv0iJswP+2/nZQ9uzA3Azi7eTrg9L2yzXc/7ZMIA==", "dev": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true } } @@ -8295,6 +8440,19 @@ "requires": { "figures": "^1.3.5", "squeak": "^1.0.0" + }, + "dependencies": { + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + } } }, "loggly": { @@ -8327,9 +8485,9 @@ "optional": true }, "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", + "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==", "dev": true, "optional": true }, @@ -8388,7 +8546,7 @@ }, "readable-stream": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", "dev": true, "optional": true, @@ -8403,7 +8561,7 @@ }, "request": { "version": "2.75.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz", + "resolved": "http://registry.npmjs.org/request/-/request-2.75.0.tgz", "integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=", "dev": true, "optional": true, @@ -8430,13 +8588,6 @@ "tough-cookie": "~2.3.0", "tunnel-agent": "~0.4.1" } - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true, - "optional": true } } }, @@ -8459,7 +8610,7 @@ "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, "lpad-align": { @@ -8481,12 +8632,6 @@ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", "dev": true }, - "macaddress": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", - "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", - "dev": true - }, "mailcomposer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz", @@ -8516,6 +8661,16 @@ "tsscmp": "~1.0.0" }, "dependencies": { + "combined-stream": { + "version": "1.0.6", + "resolved": "http://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "dev": true, + "optional": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -8543,7 +8698,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { "pify": "^3.0.0" @@ -8560,7 +8715,7 @@ "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -8579,9 +8734,9 @@ "dev": true }, "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", "dev": true }, "map-visit": { @@ -8595,7 +8750,7 @@ }, "marked": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.2.tgz", + "resolved": "http://registry.npmjs.org/marked/-/marked-0.3.2.tgz", "integrity": "sha1-AV2xWIZEOPJKZL3WGgQotBhwbQk=", "dev": true }, @@ -8614,7 +8769,7 @@ "mdn-data": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", - "integrity": "sha1-ULXU/8RXUnZXPE7tuHgIEqhBnwE=", + "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", "dev": true }, "media-typer": { @@ -8643,7 +8798,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -8666,8 +8821,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -8682,7 +8837,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -8719,7 +8874,7 @@ "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -8744,18 +8899,18 @@ "dev": true }, "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha1-o0kgUKXLm2NFBUHjnZeI0icng9s=", + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", + "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==", "dev": true }, "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha1-bzI/YKg9ERRvgx/xH9ZuL+VQO7g=", + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", + "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", "dev": true, "requires": { - "mime-db": "~1.33.0" + "mime-db": "~1.36.0" } }, "mimic-fn": { @@ -8767,7 +8922,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -8775,14 +8930,14 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -8792,7 +8947,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -8802,7 +8957,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -8811,7 +8966,7 @@ }, "morgan": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.6.1.tgz", + "resolved": "http://registry.npmjs.org/morgan/-/morgan-1.6.1.tgz", "integrity": "sha1-X9gYOYxoGcuiinzWZk8pL+HAu/I=", "dev": true, "requires": { @@ -8824,7 +8979,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -8871,16 +9026,16 @@ "dev": true }, "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", + "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", "dev": true, "optional": true }, "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha1-h59xUMstq3pHElkGbBBO7m4Pp8I=", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -8888,7 +9043,6 @@ "define-property": "^2.0.2", "extend-shallow": "^3.0.2", "fragment-cache": "^0.2.1", - "is-odd": "^2.0.0", "is-windows": "^1.0.2", "kind-of": "^6.0.2", "object.pick": "^1.3.0", @@ -8898,9 +9052,9 @@ } }, "natives": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.3.tgz", - "integrity": "sha1-RKV5vmRQfqLW7RygSpQVkVz3VVg=", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.5.tgz", + "integrity": "sha512-1pJ+02gl2KJgCPFtpZGtuD4lGSJnIZvvFHCQTOeDRMSXjfu2GmYWuhI8NFMA4W2I5NNFRbfy/YCiVt4CgNpP8A==", "dev": true }, "natural-compare": { @@ -8923,9 +9077,9 @@ "optional": true }, "nice-try": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", - "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "node-fs": { @@ -8965,6 +9119,13 @@ "socks": "1.1.9" }, "dependencies": { + "smart-buffer": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", + "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", + "dev": true, + "optional": true + }, "socks": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz", @@ -9046,7 +9207,7 @@ "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", @@ -9082,6 +9243,2702 @@ "sort-keys": "^1.0.0" } }, + "npm": { + "version": "6.4.1", + "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", + "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" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.4", + "bundled": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.4.1", + "bundled": true, + "requires": { + "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" + } + }, + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "archy": { + "version": "1.0.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true + }, + "aws4": { + "version": "1.8.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "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" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.5.1", + "bundled": true + }, + "boxen": { + "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" + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "bundled": true + }, + "builtin-modules": { + "version": "1.1.1", + "bundled": true + }, + "builtins": { + "version": "1.0.3", + "bundled": true + }, + "byline": { + "version": "5.0.0", + "bundled": true + }, + "byte-size": { + "version": "4.0.3", + "bundled": true + }, + "cacache": { + "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" + } + }, + "call-limit": { + "version": "1.1.0", + "bundled": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "chalk": { + "version": "2.4.1", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true + }, + "ci-info": { + "version": "1.4.0", + "bundled": true + }, + "cidr-regex": { + "version": "2.0.9", + "bundled": true, + "requires": { + "ip-regex": "^2.1.0" + } + }, + "cli-boxes": { + "version": "1.0.0", + "bundled": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "requires": { + "string-width": "^2.0.0", + "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" + } + }, + "cliui": { + "version": "4.1.0", + "bundled": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true + }, + "cmd-shim": { + "version": "2.0.2", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "~0.5.0" + } + }, + "co": { + "version": "4.6.0", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "color-convert": { + "version": "1.9.1", + "bundled": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "colors": { + "version": "1.1.2", + "bundled": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.6", + "bundled": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "concat-stream": { + "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" + } + }, + "config-chain": { + "version": "1.1.11", + "bundled": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "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" + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "copy-concurrently": { + "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" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "create-error-class": { + "version": "3.0.2", + "bundled": true, + "requires": { + "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" + } + }, + "crypto-random-string": { + "version": "1.0.0", + "bundled": true + }, + "cyclist": { + "version": "0.2.2", + "bundled": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-indent": { + "version": "5.0.0", + "bundled": true + }, + "detect-newline": { + "version": "2.1.0", + "bundled": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "dot-prop": { + "version": "4.2.0", + "bundled": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotenv": { + "version": "5.0.1", + "bundled": true + }, + "duplexer3": { + "version": "0.1.4", + "bundled": true + }, + "duplexify": { + "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" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "editor": { + "version": "1.0.0", + "bundled": true + }, + "encoding": { + "version": "0.1.12", + "bundled": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "^1.4.0" + } + }, + "err-code": { + "version": "1.1.2", + "bundled": true + }, + "errno": { + "version": "0.1.7", + "bundled": true, + "requires": { + "prr": "~1.0.1" + } + }, + "es6-promise": { + "version": "4.2.4", + "bundled": true + }, + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true + }, + "execa": { + "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" + } + }, + "extend": { + "version": "3.0.2", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "bundled": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "bundled": true + }, + "figgy-pudding": { + "version": "3.4.1", + "bundled": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.3.2", + "bundled": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "path-is-inside": "^1.0.1", + "rimraf": "^2.5.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" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "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" + }, + "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" + } + } + } + }, + "genfun": { + "version": "4.0.1", + "bundled": true + }, + "gentle-fs": { + "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" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "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" + } + }, + "global-dirs": { + "version": "0.1.1", + "bundled": true, + "requires": { + "ini": "^1.3.4" + } + }, + "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" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true + }, + "har-validator": { + "version": "5.1.0", + "bundled": true, + "requires": { + "ajv": "^5.3.0", + "har-schema": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hosted-git-info": { + "version": "2.7.1", + "bundled": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.1", + "bundled": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "iferr": { + "version": "1.0.2", + "bundled": true + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "init-package-json": { + "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" + } + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true + }, + "ip": { + "version": "1.1.5", + "bundled": true + }, + "ip-regex": { + "version": "2.1.0", + "bundled": true + }, + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-ci": { + "version": "1.1.0", + "bundled": true, + "requires": { + "ci-info": "^1.0.0" + } + }, + "is-cidr": { + "version": "2.0.6", + "bundled": true, + "requires": { + "cidr-regex": "^2.0.8" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "bundled": true + }, + "is-obj": { + "version": "1.0.1", + "bundled": true + }, + "is-path-inside": { + "version": "1.0.1", + "bundled": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "bundled": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "bundled": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "latest-version": { + "version": "3.1.0", + "bundled": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "requires": { + "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" + } + }, + "libnpmhook": { + "version": "4.0.1", + "bundled": true, + "requires": { + "figgy-pudding": "^3.1.0", + "npm-registry-fetch": "^3.0.0" + }, + "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" + } + } + } + }, + "libnpx": { + "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" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "requires": { + "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" + } + }, + "lockfile": { + "version": "1.0.4", + "bundled": true, + "requires": { + "signal-exit": "^3.0.2" + } + }, + "lodash._baseindexof": { + "version": "3.1.0", + "bundled": true + }, + "lodash._baseuniq": { + "version": "4.6.0", + "bundled": true, + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "bundled": true + }, + "lodash._cacheindexof": { + "version": "3.0.2", + "bundled": true + }, + "lodash._createcache": { + "version": "3.1.2", + "bundled": true, + "requires": { + "lodash._getnative": "^3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "bundled": true + }, + "lodash._getnative": { + "version": "3.9.1", + "bundled": true + }, + "lodash._root": { + "version": "3.0.1", + "bundled": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "bundled": true + }, + "lodash.restparam": { + "version": "3.6.1", + "bundled": true + }, + "lodash.union": { + "version": "4.6.0", + "bundled": true + }, + "lodash.uniq": { + "version": "4.5.0", + "bundled": true + }, + "lodash.without": { + "version": "4.4.0", + "bundled": true + }, + "lowercase-keys": { + "version": "1.0.1", + "bundled": true + }, + "lru-cache": { + "version": "4.1.3", + "bundled": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "bundled": true, + "requires": { + "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" + } + }, + "meant": { + "version": "1.0.1", + "bundled": true + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "mime-db": { + "version": "1.35.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.19", + "bundled": true, + "requires": { + "mime-db": "~1.35.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "minipass": { + "version": "2.3.3", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "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" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "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" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true + }, + "mute-stream": { + "version": "0.0.7", + "bundled": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "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" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "bundled": true, + "requires": { + "abbrev": "1" + } + }, + "semver": { + "version": "5.3.0", + "bundled": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + } + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "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" + } + }, + "npm-audit-report": { + "version": "1.3.1", + "bundled": true, + "requires": { + "cli-table3": "^0.5.0", + "console-control-strings": "^1.1.0" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true + }, + "npm-install-checks": { + "version": "3.0.0", + "bundled": true, + "requires": { + "semver": "^2.3.0 || 3.x || 4 || 5" + } + }, + "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", + "uid-number": "0.0.6", + "umask": "^1.1.0", + "which": "^1.3.1" + } + }, + "npm-logical-tree": { + "version": "1.2.1", + "bundled": true + }, + "npm-package-arg": { + "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" + } + }, + "npm-packlist": { + "version": "1.1.11", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "2.1.0", + "bundled": true, + "requires": { + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-profile": { + "version": "3.0.2", + "bundled": true, + "requires": { + "aproba": "^1.1.2 || 2", + "make-fetch-happen": "^2.5.0 || 3 || 4" + } + }, + "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" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "npm-registry-fetch": { + "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" + }, + "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" + }, + "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" + } + } + } + }, + "figgy-pudding": { + "version": "2.0.1", + "bundled": true + }, + "make-fetch-happen": { + "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" + } + }, + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "smart-buffer": { + "version": "1.1.15", + "bundled": true + }, + "socks": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ip": "^1.1.4", + "smart-buffer": "^1.0.13" + } + }, + "socks-proxy-agent": { + "version": "3.0.1", + "bundled": true, + "requires": { + "agent-base": "^4.1.0", + "socks": "^1.1.10" + } + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.0", + "bundled": true + }, + "npmlog": { + "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" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.9.0", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.0", + "bundled": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "p-limit": { + "version": "1.2.0", + "bundled": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "bundled": true + }, + "package-json": { + "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" + } + }, + "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" + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "path-is-inside": { + "version": "1.0.2", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true + }, + "pify": { + "version": "3.0.0", + "bundled": true + }, + "prepend-http": { + "version": "1.0.4", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + } + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1" + } + }, + "proto-list": { + "version": "1.2.4", + "bundled": true + }, + "protoduck": { + "version": "5.0.0", + "bundled": true, + "requires": { + "genfun": "^4.0.1" + } + }, + "prr": { + "version": "1.0.1", + "bundled": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true + }, + "psl": { + "version": "1.1.29", + "bundled": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "bundled": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "bundled": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true + }, + "qs": { + "version": "6.5.2", + "bundled": true + }, + "query-string": { + "version": "6.1.0", + "bundled": true, + "requires": { + "decode-uri-component": "^0.2.0", + "strict-uri-encode": "^2.0.0" + } + }, + "qw": { + "version": "1.0.1", + "bundled": true + }, + "rc": { + "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" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "1.0.1", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "registry-auth-token": { + "version": "3.3.2", + "bundled": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "requires": { + "rc": "^1.0.1" + } + }, + "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" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true + }, + "retry": { + "version": "0.12.0", + "bundled": true + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "^7.0.5" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "semver": { + "version": "5.5.0", + "bundled": true + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "requires": { + "semver": "^5.0.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "sha": { + "version": "2.0.1", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "readable-stream": "^2.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "slash": { + "version": "1.0.0", + "bundled": true + }, + "slide": { + "version": "1.1.6", + "bundled": true + }, + "smart-buffer": { + "version": "4.0.1", + "bundled": true + }, + "socks": { + "version": "2.2.0", + "bundled": true, + "requires": { + "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" + } + }, + "sorted-object": { + "version": "2.0.1", + "bundled": true + }, + "sorted-union-stream": { + "version": "2.1.3", + "bundled": true, + "requires": { + "from2": "^1.3.0", + "stream-iterate": "^1.1.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.10" + } + }, + "isarray": { + "version": "0.0.1", + "bundled": true + }, + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true + } + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "bundled": true + }, + "sshpk": { + "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" + } + }, + "ssri": { + "version": "6.0.0", + "bundled": true + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "stream-shift": "^1.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "bundled": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-package": { + "version": "1.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "supports-color": { + "version": "5.4.0", + "bundled": true, + "requires": { + "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" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "term-size": { + "version": "1.2.0", + "bundled": true, + "requires": { + "execa": "^0.7.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true + }, + "through": { + "version": "2.3.8", + "bundled": true + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "timed-out": { + "version": "4.0.1", + "bundled": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true + }, + "tough-cookie": { + "version": "2.4.3", + "bundled": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "bundled": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true + }, + "umask": { + "version": "1.1.0", + "bundled": true + }, + "unique-filename": { + "version": "1.1.0", + "bundled": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "bundled": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "bundled": true + }, + "unzip-response": { + "version": "2.0.1", + "bundled": true + }, + "update-notifier": { + "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" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "util-extend": { + "version": "1.0.3", + "bundled": true + }, + "uuid": { + "version": "3.3.2", + "bundled": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "requires": { + "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" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "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" + } + } + } + }, + "widest-line": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "worker-farm": { + "version": "1.6.0", + "bundled": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "requires": { + "string-width": "^1.0.1", + "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" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "2.3.0", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "bundled": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yallist": { + "version": "2.1.2", + "bundled": true + }, + "yargs": { + "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" + }, + "dependencies": { + "y18n": { + "version": "3.2.1", + "bundled": true + } + } + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -9166,7 +12023,7 @@ "object-keys": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha1-CcU4VTd1dTEMymL1W7M0q/97PtI=", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", "dev": true }, "object-visit": { @@ -9279,10 +12136,13 @@ } }, "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } }, "open": { "version": "0.0.5", @@ -9298,6 +12158,14 @@ "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } } }, "optionator": { @@ -9312,14 +12180,6 @@ "prelude-ls": "~1.1.2", "type-check": "~0.3.2", "wordwrap": "~1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } } }, "optipng-bin": { @@ -9383,9 +12243,9 @@ "dev": true }, "pac-proxy-agent": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-2.0.2.tgz", - "integrity": "sha512-cDNAN1Ehjbf5EHkNY5qnRhGPUCp6SnpyVof5fRzN800QV1Y2OkzbH9rmjZkbBRa8igof903yOnjIl6z0SlAhxA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.0.tgz", + "integrity": "sha512-AOUX9jES/EkQX2zRz0AW7lSx9jD//hQS8wFXBvcnd/J2Py9KaMJMqV/LPqJssj1tgGufotb2mmopGPR15ODv1Q==", "dev": true, "optional": true, "requires": { @@ -9396,7 +12256,7 @@ "https-proxy-agent": "^2.2.1", "pac-resolver": "^3.0.0", "raw-body": "^2.2.0", - "socks-proxy-agent": "^3.0.0" + "socks-proxy-agent": "^4.0.1" }, "dependencies": { "bytes": { @@ -9407,13 +12267,13 @@ "optional": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "depd": { @@ -9425,7 +12285,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "optional": true, @@ -9446,6 +12306,13 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true, + "optional": true + }, "raw-body": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", @@ -9458,17 +12325,6 @@ "iconv-lite": "0.4.23", "unpipe": "1.0.0" } - }, - "socks-proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz", - "integrity": "sha512-ZwEDymm204mTzvdqyUqOdovVr2YRd2NYskrYrF2LXyZ9qDiMAoFESGK8CRphiO7rtbo2Y757k2Nia3x2hGtalA==", - "dev": true, - "optional": true, - "requires": { - "agent-base": "^4.1.0", - "socks": "^1.1.10" - } } } }, @@ -9484,6 +12340,15 @@ "ip": "^1.1.5", "netmask": "^1.0.6", "thunkify": "^2.1.2" + }, + "dependencies": { + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "optional": true + } } }, "parse-filepath": { @@ -9611,9 +12476,9 @@ "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-proxy": { @@ -9677,7 +12542,7 @@ }, "pause-stream": { "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { @@ -9714,9 +12579,15 @@ }, "dependencies": { "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha1-3EIhwrFlGHYL2MOaUtjzVvwA7Sk=", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", + "dev": true + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true } } @@ -9743,55 +12614,15 @@ } }, "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", "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" - }, - "dependencies": { - "arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" - } - }, - "arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", - "dev": true - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, - "extend-shallow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", - "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", - "dev": true, - "requires": { - "kind-of": "^1.1.0" - } - }, - "kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", - "dev": true - } + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" } }, "plugin-log": { @@ -9840,7 +12671,7 @@ "postcss": { "version": "5.2.18", "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { "chalk": "^1.1.3", @@ -9851,7 +12682,7 @@ }, "postcss-calc": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", "dev": true, "requires": { @@ -9883,7 +12714,7 @@ }, "postcss-discard-comments": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "resolved": "http://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", "dev": true, "requires": { @@ -9901,7 +12732,7 @@ }, "postcss-discard-empty": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", "dev": true, "requires": { @@ -9910,7 +12741,7 @@ }, "postcss-discard-overridden": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", "dev": true, "requires": { @@ -9919,7 +12750,7 @@ }, "postcss-discard-unused": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "resolved": "http://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", "dev": true, "requires": { @@ -9928,13 +12759,12 @@ } }, "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", "dev": true, "requires": { - "postcss": "^5.0.4", - "uniqid": "^4.0.0" + "postcss": "^5.0.4" } }, "postcss-load-config": { @@ -9971,7 +12801,7 @@ }, "postcss-merge-idents": { "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "resolved": "http://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", "dev": true, "requires": { @@ -10010,7 +12840,7 @@ }, "postcss-minify-font-values": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "resolved": "http://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", "dev": true, "requires": { @@ -10021,7 +12851,7 @@ }, "postcss-minify-gradients": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "resolved": "http://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", "dev": true, "requires": { @@ -10031,7 +12861,7 @@ }, "postcss-minify-params": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "resolved": "http://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", "dev": true, "requires": { @@ -10043,7 +12873,7 @@ }, "postcss-minify-selectors": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", "dev": true, "requires": { @@ -10055,7 +12885,7 @@ }, "postcss-normalize-charset": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", "dev": true, "requires": { @@ -10064,7 +12894,7 @@ }, "postcss-normalize-url": { "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "resolved": "http://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", "dev": true, "requires": { @@ -10086,7 +12916,7 @@ }, "postcss-reduce-idents": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "resolved": "http://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", "dev": true, "requires": { @@ -10096,7 +12926,7 @@ }, "postcss-reduce-initial": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", "dev": true, "requires": { @@ -10105,7 +12935,7 @@ }, "postcss-reduce-transforms": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "resolved": "http://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", "dev": true, "requires": { @@ -10127,7 +12957,7 @@ }, "postcss-svgo": { "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "resolved": "http://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", "dev": true, "requires": { @@ -10139,7 +12969,7 @@ }, "postcss-unique-selectors": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "resolved": "http://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", "dev": true, "requires": { @@ -10156,7 +12986,7 @@ }, "postcss-zindex": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", "dev": true, "requires": { @@ -10198,19 +13028,19 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", "dev": true }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dev": true, "optional": true, "requires": { @@ -10228,9 +13058,9 @@ } }, "proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.1.tgz", - "integrity": "sha512-mAZexaz9ZxQhYPWfAjzlrloEjW+JHiBFryE4AJXFDTnaXfmH/FKqC1swTRKuEPbHWz02flQNXFOyDUF7zfEG6A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.3.tgz", + "integrity": "sha512-PXVVVuH9tiQuxQltFJVSnXWuDtNr+8aNBP6XVDDCDiUuDN8eRCm+ii4/mFWmXWEA0w8jjJSlePa4LXlM4jIzNA==", "dev": true, "optional": true, "requires": { @@ -10239,19 +13069,19 @@ "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.1", "lru-cache": "^4.1.2", - "pac-proxy-agent": "^2.0.1", + "pac-proxy-agent": "^3.0.0", "proxy-from-env": "^1.0.0", "socks-proxy-agent": "^4.0.1" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "lru-cache": { @@ -10264,6 +13094,13 @@ "pseudomap": "^1.0.2", "yallist": "^2.1.2" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true, + "optional": true } } }, @@ -10289,9 +13126,9 @@ "optional": true }, "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, "q": { @@ -10329,9 +13166,9 @@ "dev": true }, "randomatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", - "integrity": "sha1-01SQAw6091eN4pLObfsEqRoSiSM=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", + "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", "dev": true, "requires": { "is-number": "^4.0.0", @@ -10342,7 +13179,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true } } @@ -10372,7 +13209,7 @@ }, "iconv-lite": { "version": "0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", "dev": true } @@ -10381,7 +13218,7 @@ "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha1-zZJL9SAKB1uDwYjNa54hG3/A0+0=", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { "deep-extend": "^0.6.0", @@ -10392,7 +13229,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -10416,8 +13253,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -10432,7 +13269,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -10463,7 +13300,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -10474,15 +13311,14 @@ } }, "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" }, "dependencies": { "graceful-fs": { @@ -10499,8 +13335,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -10515,7 +13351,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -10570,7 +13406,7 @@ }, "reduce-css-calc": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "resolved": "http://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "dev": true, "requires": { @@ -10607,7 +13443,7 @@ "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { "is-equal-shallow": "^0.1.3" @@ -10616,22 +13452,13 @@ "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" } }, - "regexp.prototype.flags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", - "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2" - } - }, "regexpp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", @@ -10645,9 +13472,9 @@ "dev": true }, "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", "dev": true }, "repeat-string": { @@ -10706,6 +13533,15 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } } } }, @@ -10754,9 +13590,9 @@ "dev": true }, "resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha1-qt1lY3T9KYruiVvAJrgpdBhnf9M=", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", "dev": true, "requires": { "path-parse": "^1.0.5" @@ -10810,23 +13646,12 @@ "requires": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" - }, - "dependencies": { - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - } } }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, "right-align": { @@ -10841,7 +13666,7 @@ "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { "glob": "^7.0.5" @@ -10865,27 +13690,79 @@ "run-sequence": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz", - "integrity": "sha1-HOZD2jb9jH6n4akynaM/wriJhJU=", + "integrity": "sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw==", "dev": true, "requires": { "chalk": "^1.1.3", "fancy-log": "^1.3.2", "plugin-error": "^0.1.2" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "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" + } + } } }, "rxjs": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", - "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "symbol-observable": "1.0.1" + "tslib": "^1.9.0" } }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "safe-regex": { @@ -10906,7 +13783,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, "seek-bzip": { @@ -10942,9 +13819,9 @@ }, "dependencies": { "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha1-3Eu8emyp2Rbe5dQ1FvAJK1j3uKs=", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true, "optional": true } @@ -10972,7 +13849,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -11042,7 +13919,7 @@ "dependencies": { "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -11077,7 +13954,7 @@ "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -11156,11 +14033,10 @@ } }, "smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", - "dev": true, - "optional": true + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz", + "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==", + "dev": true }, "smtp-connection": { "version": "2.12.0", @@ -11175,7 +14051,7 @@ "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { "base": "^0.11.1", @@ -11211,7 +14087,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { "define-property": "^1.0.0", @@ -11231,7 +14107,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -11240,7 +14116,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -11249,7 +14125,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -11262,7 +14138,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { "kind-of": "^3.2.0" @@ -11358,14 +14234,13 @@ } }, "socks": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", - "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.1.tgz", + "integrity": "sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w==", "dev": true, - "optional": true, "requires": { - "ip": "^1.1.4", - "smart-buffer": "^1.0.13" + "ip": "^1.1.5", + "smart-buffer": "^4.0.1" } }, "socks-proxy-agent": { @@ -11373,30 +14248,9 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", "dev": true, - "optional": true, "requires": { "agent-base": "~4.2.0", "socks": "~2.2.0" - }, - "dependencies": { - "smart-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz", - "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==", - "dev": true, - "optional": true - }, - "socks": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.1.tgz", - "integrity": "sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w==", - "dev": true, - "optional": true, - "requires": { - "ip": "^1.1.5", - "smart-buffer": "^4.0.1" - } - } } }, "sort-keys": { @@ -11415,12 +14269,12 @@ "dev": true }, "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha1-etD1k/IoFZjoVN+A8ZquS5LXoRo=", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "atob": "^2.0.0", + "atob": "^2.1.1", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", "source-map-url": "^0.4.0", @@ -11434,15 +14288,15 @@ "dev": true }, "sparkles": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", - "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", "dev": true }, "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha1-BaW01xU6GVvJLDxCW2nzsqlSTII=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.1.tgz", + "integrity": "sha512-hxSPZbRZvSDuOvADntOElzJpenIR7wXJkuoUcUtS0erbgt2fgeaoPIYretfKpslMhfFDY4k0MZ2F5CUzhBsSvQ==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -11450,15 +14304,15 @@ } }, "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha1-LHrmEFbHFKW5ubKyr30xHvXHj+k=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", "dev": true }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -11466,15 +14320,15 @@ } }, "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha1-enzShHDMbToc/m1miG9rxDDTrIc=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", + "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==", "dev": true }, "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "dev": true, "requires": { "through": "2" @@ -11483,7 +14337,7 @@ "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { "extend-shallow": "^3.0.0" @@ -11508,9 +14362,9 @@ } }, "sshpk": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", - "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "dev": true, "requires": { "asn1": "~0.2.3", @@ -11520,6 +14374,7 @@ "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, "dependencies": { @@ -11534,7 +14389,7 @@ "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha1-g26zyDgv4pNv6vVEYxAXzn1Ho88=", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true, "optional": true }, @@ -11572,12 +14427,13 @@ "dev": true }, "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "version": "0.2.2", + "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", "dev": true, "requires": { - "duplexer": "~0.1.1" + "duplexer": "~0.1.1", + "through": "~2.3.4" } }, "stream-combiner2": { @@ -11607,8 +14463,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -11623,7 +14479,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -11634,7 +14490,7 @@ "stream-consume": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", - "integrity": "sha1-0721mMK9CugrjKx6xQsRB6eZbEg=", + "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==", "dev": true }, "stream-counter": { @@ -11665,12 +14521,12 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "isarray": { @@ -11679,9 +14535,15 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -11738,19 +14600,6 @@ } } }, - "string.prototype.matchall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz", - "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.10.0", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "regexp.prototype.flags": "^1.2.0" - } - }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -11758,14 +14607,14 @@ "dev": true }, "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", "dev": true }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -11783,54 +14632,15 @@ } }, "strip-bom-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", - "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", "dev": true, "requires": { - "first-chunk-stream": "^2.0.0", + "first-chunk-stream": "^1.0.0", "strip-bom": "^2.0.0" }, "dependencies": { - "first-chunk-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", - "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -11844,7 +14654,7 @@ }, "strip-dirs": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", "integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=", "dev": true, "requires": { @@ -11873,7 +14683,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -11904,7 +14714,7 @@ "strip-outer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.2" @@ -11951,15 +14761,9 @@ } } }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - }, "table": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz", "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "requires": { @@ -11971,18 +14775,6 @@ "string-width": "^2.1.1" }, "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "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.1" - } - }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -12010,9 +14802,9 @@ "dev": true }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -12021,24 +14813,24 @@ } }, "tar-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", - "integrity": "sha1-+E7xaWJp1iI8pI9uHu7eP36B85U=", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, "requires": { "bl": "^1.0.0", - "buffer-alloc": "^1.1.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.0", + "to-buffer": "^1.1.1", "xtend": "^4.0.0" }, "dependencies": { "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -12052,8 +14844,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -12068,7 +14860,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -12108,7 +14900,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -12130,8 +14922,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -12146,7 +14938,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -12256,7 +15048,7 @@ }, "debug": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", "dev": true, "requires": { @@ -12271,7 +15063,7 @@ }, "iconv-lite": { "version": "0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", "dev": true }, @@ -12327,7 +15119,7 @@ "to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", "dev": true }, "to-object-path": { @@ -12353,7 +15145,7 @@ "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { "define-property": "^2.0.2", @@ -12375,10 +15167,18 @@ "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha1-7GDO44rGdQY//JelwYlwV47oNlU=", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "dev": true, "requires": { "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } } }, "trim-newlines": { @@ -12402,6 +15202,12 @@ "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", "dev": true }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, "tsscmp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", @@ -12409,13 +15215,10 @@ "dev": true }, "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true }, "tweetnacl": { "version": "0.14.5", @@ -12436,7 +15239,7 @@ "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "dev": true, "requires": { "media-typer": "0.3.0", @@ -12535,15 +15338,6 @@ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", "dev": true }, - "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "dev": true, - "requires": { - "macaddress": "^0.2.8" - } - }, "uniqs": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", @@ -12634,14 +15428,6 @@ "dev": true, "requires": { "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - } } }, "urix": { @@ -12670,13 +15456,10 @@ } }, "use": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha1-FHFr8D/f79AwQK71jYtLhfOnxUQ=", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true }, "user-home": { "version": "1.1.1", @@ -12711,7 +15494,7 @@ "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha1-RA9xZaRZyaFtwUXrjnLzVocJcDA=", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", "dev": true, "optional": true, "requires": { @@ -12726,9 +15509,9 @@ "dev": true }, "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha1-EsUou51Y0LkmXZovbw/ovhf/HxQ=", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", "dev": true }, "uws": { @@ -12754,9 +15537,9 @@ "dev": true }, "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha1-gWQ7y+8b3+zUYjeT3EZIlIupgzg=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -12772,7 +15555,7 @@ "vendors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", - "integrity": "sha1-f8te759WI7FWvOqJ7DfWNnbyGAE=", + "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", "dev": true }, "verror": { @@ -12829,8 +15612,8 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -12845,7 +15628,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -12876,12 +15659,51 @@ "vinyl": "^1.1.0" }, "dependencies": { + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "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" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -12891,6 +15713,16 @@ "is-utf8": "^0.2.0" } }, + "strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", + "dev": true, + "requires": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, "vinyl": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", @@ -12928,7 +15760,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -12997,7 +15829,7 @@ "websocket-extensions": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha1-XS/yKXcAPsaHpLhwc9+7rBRszyk=", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", "dev": true }, "when": { @@ -13013,9 +15845,9 @@ "dev": true }, "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -13035,9 +15867,9 @@ "optional": true }, "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, "wrap-fn": { @@ -13047,14 +15879,6 @@ "dev": true, "requires": { "co": "3.1.0" - }, - "dependencies": { - "co": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", - "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", - "dev": true - } } }, "wrappy": { @@ -13111,7 +15935,7 @@ }, "yargs": { "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "requires": { @@ -13119,15 +15943,24 @@ "cliui": "^2.1.0", "decamelize": "^1.0.0", "window-size": "0.1.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + } } }, "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, "requires": { - "fd-slicer": "~1.0.1" + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" } }, "yeast": { diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 033d63e405..c05401a05c 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -4,7 +4,9 @@ "test": "karma start test/config/karma.conf.js --singlerun", "build": "gulp" }, - "dependencies": {}, + "dependencies": { + "npm": "^6.4.1" + }, "devDependencies": { "autoprefixer": "^6.5.0", "bower-installer": "^1.2.0", From b2ec2ac592d7050bd25d2c8aae13a35cd600ab84 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 3 Oct 2018 14:51:04 +0100 Subject: [PATCH 39/49] Update bower to angular 1.7.4 --- src/Umbraco.Web.UI.Client/bower.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/bower.json b/src/Umbraco.Web.UI.Client/bower.json index 9628cddfac..9f9c4fff01 100644 --- a/src/Umbraco.Web.UI.Client/bower.json +++ b/src/Umbraco.Web.UI.Client/bower.json @@ -16,13 +16,13 @@ "tests" ], "dependencies": { - "angular": "~1.7.2", - "angular-cookies": "~1.7.2", - "angular-sanitize": "~1.7.2", - "angular-touch": "~1.7.2", - "angular-route": "~1.7.2", - "angular-animate": "~1.7.2", - "angular-i18n": "~1.7.2", + "angular": "~1.7.4", + "angular-cookies": "~1.7.4", + "angular-sanitize": "~1.7.4", + "angular-touch": "~1.7.4", + "angular-route": "~1.7.4", + "angular-animate": "~1.7.4", + "angular-i18n": "~1.7.4", "signalr": "^2.2.1", "typeahead.js": "~0.10.5", "underscore": "~1.9.1", From 6d604a37b623ed999126052438debb0d854d1734 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 3 Oct 2018 14:55:15 +0100 Subject: [PATCH 40/49] WIP with Shan - Removes nested content app form validation error messages & then puts them back --- .../components/content/edit.controller.js | 112 +++++++++++++++--- .../validation/valformmanager.directive.js | 18 +-- .../validation/valsubview.directive.js | 25 +++- .../editor/umb-editor-navigation.html | 5 +- .../editor/umb-editor-sub-view.html | 12 +- 5 files changed, 143 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 3eac5439fd..fb86389eda 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -3,7 +3,7 @@ function ContentEditController($rootScope, $scope, $routeParams, $q, $window, appState, contentResource, entityResource, navigationService, notificationsService, - serverValidationManager, contentEditingHelper, treeService, formHelper, umbRequestHelper, + serverValidationManager, contentEditingHelper, treeService, formHelper, umbRequestHelper, editorState, $http, eventsService, relationResource, overlayService) { var evts = []; @@ -56,11 +56,11 @@ } bindEvents(); - - resetVariantFlags(); + + resetVariantFlags(); } - + /** * This will reset isDirty flags if save is true. * When working with multiple variants, this will set the save/publish flags of each one to false. @@ -85,7 +85,7 @@ $scope.content.variants[0].publish = false; } } - + /** Returns true if the save/publish dialog should be shown when pressing the button */ function showSaveOrPublishDialog() { return $scope.content.variants.length > 1; @@ -152,7 +152,7 @@ */ function createButtons(content, app) { - // only create the save/publish/preview buttons if the + // only create the save/publish/preview buttons if the // content app is "Conent" if(app && app.alias !== "umbContent" && app.alias !== "umbInfo") { $scope.defaultButton = null; @@ -172,7 +172,7 @@ unPublish: $scope.unPublish } }); - + $scope.defaultButton = buttons.defaultButton; $scope.subButtons = buttons.subButtons; $scope.page.showPreviewButton = true; @@ -202,7 +202,7 @@ if (infiniteMode || !path) { return; } - + if (!$scope.content.isChildOfListView) { navigationService.syncTree({ tree: $scope.treeAlias, path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) { $scope.page.menu.currentNode = syncArgs.node; @@ -213,7 +213,7 @@ //it's a child item, just sync the ui node to the parent navigationService.syncTree({ tree: $scope.treeAlias, path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true }); - //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node + //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node // from the server so that we can load in the actions menu. umbRequestHelper.resourcePromise( $http.get(content.treeNodeUrl), @@ -223,11 +223,91 @@ } } + function checkValidility(){ + //Get all controls from the 'contentForm' + var allControls = $scope.contentForm.$getControls(); + + //An array to store items in when we find child form fields (no matter how many deep nested forms) + var childFieldsToMarkAsValid = []; + + //Exclude known formControls 'contentHeaderForm' and 'tabbedContentForm' + //Check property - $name === "contentHeaderForm" + allControls = _.filter(allControls, function(obj){ + return obj.$name !== 'contentHeaderForm' && obj.$name !== 'tabbedContentForm' && obj.hasOwnProperty('$submitted'); + }); + + for (var i = 0; i < allControls.length; i++) { + var nestedForm = allControls[i]; + + //Get Nested Controls of this form in the loop + var nestedFormControls = nestedForm.$getControls(); + + //Need to recurse through controls (could be more nested forms) + childFieldsToMarkAsValid = recurseFormControls(nestedFormControls, childFieldsToMarkAsValid); + } + + return childFieldsToMarkAsValid; + } + + //Controls is the + function recurseFormControls(controls, array){ + + //Loop over the controls + for (var i = 0; i < controls.length; i++) { + var controlItem = controls[i]; + + //Check if the controlItem has a property '' + if(controlItem.hasOwnProperty('$submitted')){ + //This item is a form - so lets get the child controls of it & recurse again + var childFormControls = controlItem.$getControls(); + recurseFormControls(childFormControls, array); + } + else { + //We can assume its a field on a form + if(controlItem.hasOwnProperty('$error')){ + //Set the validlity of the error/s to be valid + //String of keys of error invalid messages + var errorKeys = []; + + for(var key in controlItem.$error){ + errorKeys.push(key); + controlItem.$setValidity(key, true); + } + + //Create a basic obj - storing the control item & the error keys + var obj = { 'control': controlItem, 'errorKeys': errorKeys }; + + //Push the updated control into the array - so we can set them back + array.push(obj); + } + } + } + return array; + } + + function resetNestedFieldValiation(array){ + for (var i = 0; i < array.length; i++) { + var item = array[i]; + //Item is an object containing two props + //'control' (obj) & 'errorKeys' (string array) + var fieldControl = item.control; + var fieldErrorKeys = item.errorKeys; + + for(var i = 0; i < fieldErrorKeys.length; i++) { + fieldControl.$setValidity(fieldErrorKeys[i], false); + } + } + } + // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish function performSave(args) { $scope.page.buttonGroupState = "busy"; + //Used to check validility of nested form - coming from Content Apps mostly + //Set them all to be invalid + var fieldsToRollback = checkValidility(); + eventsService.emit("content.saving", { content: $scope.content, action: args.action }); return contentEditingHelper.contentEditorPerformSave({ @@ -237,19 +317,19 @@ action: args.action, showNotifications: args.showNotifications }).then(function (data) { - //success + //success init($scope.content); - syncTreeNode($scope.content, data.path); $scope.page.buttonGroupState = "success"; eventsService.emit("content.saved", { content: $scope.content, action: args.action }); + resetNestedFieldValiation(fieldsToRollback); + return $q.when(data); }, function (err) { - syncTreeNode($scope.content, $scope.content.path); //error @@ -259,6 +339,8 @@ $scope.page.buttonGroupState = "error"; + resetNestedFieldValiation(fieldsToRollback); + return $q.reject(err); }); } @@ -400,7 +482,7 @@ overlayService.close(); } }; - + overlayService.open(dialog); } } @@ -517,9 +599,9 @@ if (!$scope.busy) { - // Chromes popup blocker will kick in if a window is opened + // Chromes popup blocker will kick in if a window is opened // without the initial scoped request. This trick will fix that. - // + // var previewWindow = $window.open('preview/?init=true', 'umbpreview'); // Build the correct path so both /#/ and #/ work. diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index 35e9005cc6..b201c877a3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -3,7 +3,7 @@ * @name umbraco.directives.directive:valFormManager * @restrict A * @require formController -* @description Used to broadcast an event to all elements inside this one to notify that form validation has +* @description Used to broadcast an event to all elements inside this one to notify that form validation has * changed. If we don't use this that means you have to put a watch for each directive on a form's validation * changing which would result in much higher processing. We need to actually watch the whole $error collection of a form * because just watching $valid or $invalid doesn't acurrately trigger form validation changing. @@ -19,7 +19,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location var SAVED_EVENT_NAME = "formSubmitted"; return { - require: ["form", "^^?valFormManager"], + require: ["form", "^^?valFormManager", "^^?valSubView"], restrict: "A", controller: function($scope) { //This exposes an API for direct use with this directive @@ -48,7 +48,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location var formCtrl = ctrls[0]; var parentFormMgr = ctrls.length > 0 ? ctrls[1] : null; - + var subView = ctrls.length > 1 ? ctrls[2] : null; var labels = {}; var labelKeys = [ @@ -83,7 +83,11 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location return sum; }, function (e) { scope.$broadcast("valStatusChanged", { form: formCtrl }); - + + if (subView){ + subView.valStatusChanged({ form: formCtrl }); + } + //find all invalid elements' .control-group's and apply the error class var inError = element.find(".control-group .ng-invalid").closest(".control-group"); inError.addClass("error"); @@ -94,7 +98,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location }); - //This tracks if the user is currently saving a new item, we use this to determine + //This tracks if the user is currently saving a new item, we use this to determine // if we should display the warning dialog that they are leaving the page - if a new item // is being saved we never want to display that dialog, this will also cause problems when there // are server side validation issues. @@ -122,7 +126,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location element.removeClass(SHOW_VALIDATION_CLASS_NAME); scope.showValidation = false; //clear form state as at this point we retrieve new data from the server - //and all validation will have cleared at this point + //and all validation will have cleared at this point formCtrl.$setPristine(); })); @@ -203,7 +207,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location $timeout(function(){ formCtrl.$setPristine(); }, 1000); - + } }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js index a65a08d17e..8f4d93090b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js @@ -10,6 +10,28 @@ function valSubViewDirective() { + function controller($scope, $element) { + //expose api + return { + valStatusChanged: function(args) { + if (!args.form.$valid) { + var subViewContent = $element.find(".ng-invalid"); + + if (subViewContent.length > 0) { + $scope.model.hasError = true; + $element.addClass("show-validation"); + } else { + $scope.model.hasError = false; + $element.removeClass("show-validation"); + } + } + else { + $scope.model.hasError = false; + } + } + } + } + function link(scope, el, attr, ctrl) { //if there are no containing form or valFormManager controllers, then we do nothing @@ -43,7 +65,8 @@ var directive = { require: ['?^^form', '?^^valFormManager'], restrict: "A", - link: link + link: link, + controller: controller }; return directive; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html index 3166992e8a..2bb6c1107b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html @@ -1,7 +1,8 @@