From 8ae27ecc27834c8461bcb7827999da5faafe349c Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Apr 2018 22:06:02 +1000 Subject: [PATCH 01/97] Updates doc type editor to be able to change update the doc type to support being culture variant --- .../services/umbdataformatter.service.js | 2 +- .../views/permissions/permissions.html | 41 ++++++++++++------- .../Models/ContentEditing/ContentTypeSave.cs | 9 ++++ .../ContentEditing/DocumentTypeDisplay.cs | 3 ++ .../Models/ContentEditing/DocumentTypeSave.cs | 2 +- ...=> ContentItemDisplayVariationResolver.cs} | 4 +- .../Models/Mapping/ContentMapperProfile.cs | 2 +- .../Mapping/ContentTypeMapperProfile.cs | 4 +- .../Mapping/ContentTypeProfileExtensions.cs | 2 +- .../Mapping/ContentTypeVariationsResolver.cs | 26 ++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 3 +- 11 files changed, 76 insertions(+), 22 deletions(-) rename src/Umbraco.Web/Models/Mapping/{VariationResolver.cs => ContentItemDisplayVariationResolver.cs} (90%) create mode 100644 src/Umbraco.Web/Models/Mapping/ContentTypeVariationsResolver.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index 439720e461..dab6cb8eda 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -37,7 +37,7 @@ var saveModel = _.pick(displayModel, 'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes', 'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed', - 'key', 'parentId', 'alias', 'path'); + 'key', 'parentId', 'alias', 'path', 'allowCultureVariant'); //TODO: Map these saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html index d9c7d185da..7df2577871 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html @@ -7,7 +7,7 @@
-
- + - +
+
+ +
+
Content Type Variation
+ Define the rules for how this content type's properties can be varied +
+
+ +
+ +
+ diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs index 3f3efa15d8..c2ec70d3dc 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs @@ -57,6 +57,15 @@ namespace Umbraco.Web.Models.ContentEditing Groups = new List>(); } + /// + /// A rule for defining how a content type can be varied + /// + /// + /// This is only supported on document types right now but in the future it could be media types too + /// + [DataMember(Name = "allowCultureVariant")] + public bool AllowCultureVariant { get; set; } + //Tabs [DataMember(Name = "groups")] public IEnumerable> Groups { get; set; } diff --git a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs index 7965b90ae0..6b82f74ca7 100644 --- a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs @@ -20,6 +20,9 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "defaultTemplate")] public EntityBasic DefaultTemplate { get; set; } + + [DataMember(Name = "allowCultureVariant")] + public bool AllowCultureVariant { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeSave.cs b/src/Umbraco.Web/Models/ContentEditing/DocumentTypeSave.cs index 3f163f860f..21164b493b 100644 --- a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/DocumentTypeSave.cs @@ -31,7 +31,7 @@ namespace Umbraco.Web.Models.ContentEditing /// public override IEnumerable Validate(ValidationContext validationContext) { - if (AllowedTemplates.Any(x => StringExtensions.IsNullOrWhiteSpace(x))) + if (AllowedTemplates.Any(x => x.IsNullOrWhiteSpace())) yield return new ValidationResult("Template value cannot be null", new[] { "AllowedTemplates" }); foreach (var v in base.Validate(validationContext)) diff --git a/src/Umbraco.Web/Models/Mapping/VariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs similarity index 90% rename from src/Umbraco.Web/Models/Mapping/VariationResolver.cs rename to src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs index 9d3de07d77..3d7890a152 100644 --- a/src/Umbraco.Web/Models/Mapping/VariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs @@ -10,11 +10,11 @@ using Language = Umbraco.Web.Models.ContentEditing.Language; namespace Umbraco.Web.Models.Mapping { - internal class VariationResolver : IValueResolver> + internal class ContentItemDisplayVariationResolver : IValueResolver> { private readonly ILocalizationService _localizationService; - public VariationResolver(ILocalizationService localizationService) + public ContentItemDisplayVariationResolver(ILocalizationService localizationService) { _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); } diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 6791afb921..9aa6495492 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -25,7 +25,7 @@ namespace Umbraco.Web.Models.Mapping var contentTreeNodeUrlResolver = new ContentTreeNodeUrlResolver(); var defaultTemplateResolver = new DefaultTemplateResolver(); var contentUrlResolver = new ContentUrlResolver(); - var variantResolver = new VariationResolver(localizationService); + var variantResolver = new ContentItemDisplayVariationResolver(localizationService); //FROM IContent TO ContentItemDisplay CreateMap() diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapperProfile.cs index c9e9554b93..bba27ce3f1 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapperProfile.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Web.Models.ContentEditing; using Umbraco.Core.Services; +using ContentVariation = Umbraco.Core.Models.ContentVariation; namespace Umbraco.Web.Models.Mapping { @@ -26,7 +27,7 @@ namespace Umbraco.Web.Models.Mapping { dest.AllowedTemplates = source.AllowedTemplates .Where(x => x != null) - .Select(s => fileService.GetTemplate(s)) + .Select(fileService.GetTemplate) .ToArray(); if (source.DefaultTemplate != null) @@ -111,6 +112,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dto => dto.AllowedTemplates, opt => opt.Ignore()) .ForMember(dto => dto.DefaultTemplate, opt => opt.Ignore()) .ForMember(display => display.Notifications, opt => opt.Ignore()) + .ForMember(display => display.AllowCultureVariant, opt => opt.MapFrom(type => type.Variations.HasFlag(ContentVariation.CultureNeutral))) .AfterMap((source, dest) => { //sync templates diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs index d67eaf8ea2..72842c5354 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs @@ -181,7 +181,7 @@ namespace Umbraco.Web.Models.Mapping // ignore, composition is managed in AfterMapContentTypeSaveToEntity .ForMember(dest => dest.ContentTypeComposition, opt => opt.Ignore()) - .ForMember(dto => dto.Variations, opt => opt.Ignore()) // fixme - change when UI supports it! + .ForMember(dto => dto.Variations, opt => opt.ResolveUsing>()) .ForMember( dest => dest.AllowedContentTypes, diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeVariationsResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeVariationsResolver.cs new file mode 100644 index 0000000000..fcfe9a47cc --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeVariationsResolver.cs @@ -0,0 +1,26 @@ +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; +using ContentVariation = Umbraco.Core.Models.ContentVariation; + +namespace Umbraco.Web.Models.Mapping +{ + internal class ContentTypeVariationsResolver : IValueResolver + where TSource : ContentTypeSave + where TDestination : IContentTypeComposition + where TSourcePropertyType : PropertyTypeBasic + { + public ContentVariation Resolve(TSource source, TDestination destination, ContentVariation destMember, ResolutionContext context) + { + //this will always be the case, a content type will always be allowed to be invariant + var result = ContentVariation.InvariantNeutral; + + if (source.AllowCultureVariant) + { + result |= ContentVariation.CultureNeutral; + } + + return result; + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 923078c0b7..160fc80751 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -238,6 +238,7 @@ + @@ -259,7 +260,7 @@ - + From c84087e96ba547ebda3d7fa4f7d616cd5e60b907 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Apr 2018 00:59:23 +1000 Subject: [PATCH 02/97] Gets the variant names working in the editor --- src/Umbraco.Web/Editors/ContentController.cs | 16 ++++++++++-- .../Editors/ContentControllerBase.cs | 26 ------------------- src/Umbraco.Web/Editors/MediaController.cs | 7 ++++- src/Umbraco.Web/Editors/MemberController.cs | 9 +++++++ .../Mapping/ContentItemDisplayNameResolver.cs | 25 ++++++++++++++++++ .../ContentItemDisplayVariationResolver.cs | 3 +++ .../Models/Mapping/ContentMapperProfile.cs | 1 + src/Umbraco.Web/Umbraco.Web.csproj | 1 + 8 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index dce9ca531d..6b1601fac2 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -943,14 +943,26 @@ namespace Umbraco.Web.Editors return content; } } - + /// /// Maps the dto property values to the persisted model /// /// private void MapPropertyValues(ContentItemSave contentItem) { - UpdateName(contentItem); + //Don't update the name if it is empty + if (contentItem.Name.IsNullOrWhiteSpace() == false) + { + //set the name according to the culture settings + if (contentItem.LanguageId.HasValue && contentItem.PersistedContent.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) + { + contentItem.PersistedContent.SetName(contentItem.LanguageId, contentItem.Name); + } + else + { + contentItem.PersistedContent.Name = contentItem.Name; + } + } //TODO: We need to support 'send to publish' diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index 70baabecf5..fdfefa6133 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -36,32 +36,6 @@ namespace Umbraco.Web.Editors return errorResponse; } - protected void UpdateName(ContentBaseItemSave contentItem) - where TPersisted : IContentBase - { - //Don't update the name if it is empty - if (contentItem.Name.IsNullOrWhiteSpace() == false) - { - contentItem.PersistedContent.Name = contentItem.Name; - } - } - - protected HttpResponseMessage PerformSort(ContentSortOrder sorted) - { - if (sorted == null) - { - return Request.CreateResponse(HttpStatusCode.NotFound); - } - - //if there's nothing to sort just return ok - if (sorted.IdSortOrder.Length == 0) - { - return Request.CreateResponse(HttpStatusCode.OK); - } - - return null; - } - /// /// Maps the dto property values to the persisted model /// diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 2c7a5f30c6..fb09cefd03 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -470,7 +470,12 @@ namespace Umbraco.Web.Editors // * we have a reference to the DTO object and the persisted object // * Permissions are valid - UpdateName(contentItem); + //Don't update the name if it is empty + if (contentItem.Name.IsNullOrWhiteSpace() == false) + { + contentItem.PersistedContent.Name = contentItem.Name; + } + MapPropertyValues( contentItem, (save, property) => property.GetValue(), //get prop val diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index bf60560574..bb99030804 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -591,6 +591,15 @@ namespace Umbraco.Web.Editors contentItem.PersistedContent.Username = providedUserName; } + private static void UpdateName(MemberSave memberSave) + { + //Don't update the name if it is empty + if (memberSave.Name.IsNullOrWhiteSpace() == false) + { + memberSave.PersistedContent.Name = memberSave.Name; + } + } + /// /// This is going to create the user with the membership provider and check for validation /// diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs new file mode 100644 index 0000000000..125db912be --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs @@ -0,0 +1,25 @@ +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; +using ContentVariation = Umbraco.Core.Models.ContentVariation; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Used to map the name from an depending on it's variation settings + /// + internal class ContentItemDisplayNameResolver : IValueResolver + { + public string Resolve(IContent source, ContentItemDisplay destination, string destMember, ResolutionContext context) + { + var langId = context.GetLanguageId(); + if (langId.HasValue && source.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) + { + //return the culture name being requested + return source.GetName(langId); + } + + return source.Name; + } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs index 3d7890a152..41146d7a8e 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs @@ -10,6 +10,9 @@ using Language = Umbraco.Web.Models.ContentEditing.Language; namespace Umbraco.Web.Models.Mapping { + /// + /// Used to map the variations collection from an instance + /// internal class ContentItemDisplayVariationResolver : IValueResolver> { private readonly ILocalizationService _localizationService; diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 9aa6495492..627f508906 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -32,6 +32,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, src.Key))) .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src))) .ForMember(dest => dest.Updater, opt => opt.ResolveUsing(src => creatorResolver.Resolve(src))) + .ForMember(dest => dest.Name, opt => opt.ResolveUsing()) .ForMember(dest => dest.Variants, opt => opt.ResolveUsing(variantResolver)) .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 7691f294db..56c04deca1 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -242,6 +242,7 @@ + From bfca8c555e328bd93ca36f9f77aa37adecaba342 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 19 Apr 2018 19:12:42 +0200 Subject: [PATCH 03/97] Removes Package repository config setting and related unnecessary code --- .../tools/umbracoSettings.config.install.xdt | 2 + .../UmbracoSettings/IRepositoriesSection.cs | 10 - .../UmbracoSettings/IRepository.cs | 14 - .../IUmbracoSettingsSection.cs | 6 +- .../UmbracoSettings/RepositoriesCollection.cs | 36 - .../UmbracoSettings/RepositoriesElement.cs | 23 - .../RepositoryConfigExtensions.cs | 19 - .../UmbracoSettings/RepositoryElement.cs | 53 - .../UmbracoSettings/UmbracoSettingsSection.cs | 50 +- .../Constants-PackageRepository.cs | 15 + src/Umbraco.Core/Services/PackagingService.cs | 6 +- src/Umbraco.Core/Umbraco.Core.csproj | 9 +- .../PackageRepositoriesElementDefaultTests.cs | 23 - .../PackageRepositoriesElementTests.cs | 27 - .../TestHelpers/SettingsForTests.cs | 2 - src/Umbraco.Tests/Umbraco.Tests.csproj | 2 - src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 +- .../config/umbracoSettings.config | 8 +- .../umbraco/developer/Packages/installer.aspx | 346 --- .../Editors/BackOfficeServerVariables.cs | 3 +- .../Editors/PackageInstallController.cs | 2 - src/Umbraco.Web/Umbraco.Web.csproj | 4 - .../umbraco/Trees/loadPackager.cs | 87 +- .../umbraco/Trees/loadPackages.cs | 139 -- .../umbraco/create/CreatedPackageTasks.cs | 2 +- .../developer/Packages/installer.aspx.cs | 835 ------- .../Packager/Repositories/Repository.cs | 299 --- .../Repositories/RepositoryWebservice.cs | 2002 ----------------- src/umbraco.cms/umbraco.cms.csproj | 2 - 29 files changed, 48 insertions(+), 3982 deletions(-) delete mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs delete mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs delete mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesCollection.cs delete mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesElement.cs delete mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryConfigExtensions.cs delete mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryElement.cs create mode 100644 src/Umbraco.Core/Constants-PackageRepository.cs delete mode 100644 src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs delete mode 100644 src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementTests.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadPackages.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs delete mode 100644 src/umbraco.cms/businesslogic/Packager/Repositories/Repository.cs delete mode 100644 src/umbraco.cms/businesslogic/Packager/Repositories/RepositoryWebservice.cs diff --git a/build/NuSpecs/tools/umbracoSettings.config.install.xdt b/build/NuSpecs/tools/umbracoSettings.config.install.xdt index a4725b835f..295fc0ba57 100644 --- a/build/NuSpecs/tools/umbracoSettings.config.install.xdt +++ b/build/NuSpecs/tools/umbracoSettings.config.install.xdt @@ -5,4 +5,6 @@ + + \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs deleted file mode 100644 index 063acbe1cf..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - - public interface IRepositoriesSection : IUmbracoConfigurationSection - { - IEnumerable Repositories { get; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs deleted file mode 100644 index 7559f090c0..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IRepository - { - string Name { get; } - Guid Id { get; } - string RepositoryUrl { get; } - string WebServiceUrl { get; } - bool HasCustomWebServiceUrl { get; } - string RestApiUrl { get; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs index 7174f7762f..ee6d172b08 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs @@ -24,9 +24,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IScheduledTasksSection ScheduledTasks { get; } IDistributedCallSection DistributedCall { get; } - - IRepositoriesSection PackageRepositories { get; } - + IProvidersSection Providers { get; } [EditorBrowsable(EditorBrowsableState.Never)] @@ -37,4 +35,4 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IScriptingSection Scripting { get; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesCollection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesCollection.cs deleted file mode 100644 index 994c808703..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesCollection.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class RepositoriesCollection : ConfigurationElementCollection, IEnumerable - { - internal void Add(RepositoryElement item) - { - BaseAdd(item); - } - - protected override ConfigurationElement CreateNewElement() - { - return new RepositoryElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((RepositoryElement)element).Id; - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as IRepository; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesElement.cs deleted file mode 100644 index e2c3ba3036..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesElement.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class RepositoriesElement : ConfigurationElement, IRepositoriesSection - { - - [ConfigurationCollection(typeof(RepositoriesCollection), AddItemName = "repository")] - [ConfigurationProperty("", IsDefaultCollection = true)] - internal RepositoriesCollection Repositories - { - get { return (RepositoriesCollection) base[""]; } - set { base[""] = value; } - } - - IEnumerable IRepositoriesSection.Repositories - { - get { return Repositories; } - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryConfigExtensions.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryConfigExtensions.cs deleted file mode 100644 index e2c4283dc6..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryConfigExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Linq; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public static class RepositoryConfigExtensions - { - //Our package repo - private static readonly Guid RepoGuid = new Guid("65194810-1f85-11dd-bd0b-0800200c9a66"); - - public static IRepository GetDefault(this IRepositoriesSection repos) - { - var found = repos.Repositories.FirstOrDefault(x => x.Id == RepoGuid); - if (found == null) - throw new InvalidOperationException("No default package repository found with id " + RepoGuid); - return found; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryElement.cs deleted file mode 100644 index a249be2ee3..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryElement.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class RepositoryElement : ConfigurationElement, IRepository - { - [ConfigurationProperty("name", IsRequired = true)] - public string Name - { - get { return (string)base["name"]; } - set { base["name"] = value; } - } - - [ConfigurationProperty("guid", IsRequired = true)] - public Guid Id - { - get { return (Guid)base["guid"]; } - set { base["guid"] = value; } - } - - [ConfigurationProperty("repositoryurl", DefaultValue = "http://packages.umbraco.org")] - public string RepositoryUrl - { - get { return (string)base["repositoryurl"]; } - set { base["repositoryurl"] = value; } - } - - [ConfigurationProperty("webserviceurl", DefaultValue = "/umbraco/webservices/api/repository.asmx")] - public string WebServiceUrl - { - get { return (string)base["webserviceurl"]; } - set { base["webserviceurl"] = value; } - } - - public bool HasCustomWebServiceUrl - { - get - { - var prop = Properties["webserviceurl"]; - return (string) prop.DefaultValue != (string) this[prop]; - } - } - - [ConfigurationProperty("restapiurl", DefaultValue = "https://our.umbraco.org/webapi/packages/v1")] - public string RestApiUrl - { - get { return (string)base["restapiurl"]; } - set { base["restapiurl"] = value; } - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs index 9ec1b36053..ef40c3f324 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -67,50 +67,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { get { return (DistributedCallElement)this["distributedCall"]; } } - - private RepositoriesElement _defaultRepositories; - - [ConfigurationProperty("repositories")] - internal RepositoriesElement PackageRepositories - { - get - { - - if (_defaultRepositories != null) - { - return _defaultRepositories; - } - - //here we need to check if this element is defined, if it is not then we'll setup the defaults - var prop = Properties["repositories"]; - var repos = this[prop] as ConfigurationElement; - if (repos != null && repos.ElementInformation.IsPresent == false) - { - var collection = new RepositoriesCollection - { - new RepositoryElement() {Name = "Umbraco package Repository", Id = new Guid("65194810-1f85-11dd-bd0b-0800200c9a66")} - }; - - - _defaultRepositories = new RepositoriesElement() - { - Repositories = collection - }; - - return _defaultRepositories; - } - - //now we need to ensure there is *always* our umbraco repo! its hard coded in the codebase! - var reposElement = (RepositoriesElement)base["repositories"]; - if (reposElement.Repositories.All(x => x.Id != new Guid("65194810-1f85-11dd-bd0b-0800200c9a66"))) - { - reposElement.Repositories.Add(new RepositoryElement() { Name = "Umbraco package Repository", Id = new Guid("65194810-1f85-11dd-bd0b-0800200c9a66") }); - } - - return reposElement; - } - } - + [ConfigurationProperty("providers")] internal ProvidersElement Providers { @@ -185,11 +142,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings get { return DistributedCall; } } - IRepositoriesSection IUmbracoSettingsSection.PackageRepositories - { - get { return PackageRepositories; } - } - IProvidersSection IUmbracoSettingsSection.Providers { get { return Providers; } diff --git a/src/Umbraco.Core/Constants-PackageRepository.cs b/src/Umbraco.Core/Constants-PackageRepository.cs new file mode 100644 index 0000000000..bdcb86932b --- /dev/null +++ b/src/Umbraco.Core/Constants-PackageRepository.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Core +{ + public static partial class Constants + { + /// + /// Defines the constants used for the Umbraco package repository + /// + public static class PackageRepository + { + public const string RestApiBaseUrl = "https://our.umbraco.org/webapi/packages/v1"; + public const string DefaultRepositoryName = "Umbraco package Repository"; + public const string DefaultRepositoryId = "65194810-1f85-11dd-bd0b-0800200c9a66"; + } + } +} diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index f9b8ab5e9a..dcef982992 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -89,13 +89,11 @@ namespace Umbraco.Core.Services /// public string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId) { - var packageRepo = UmbracoConfig.For.UmbracoSettings().PackageRepositories.GetDefault(); - using (var httpClient = new HttpClient()) using (var uow = _uowProvider.GetUnitOfWork()) { //includeHidden = true because we don't care if it's hidden we want to get the file regardless - var url = string.Format("{0}/{1}?version={2}&includeHidden=true&asFile=true", packageRepo.RestApiUrl, packageId, umbracoVersion.ToString(3)); + var url = string.Format("{0}/{1}?version={2}&includeHidden=true&asFile=true", Constants.PackageRepository.RestApiBaseUrl, packageId, umbracoVersion.ToString(3)); byte[] bytes; try { @@ -124,7 +122,7 @@ namespace Umbraco.Core.Services } } - Audit(uow, AuditType.PackagerInstall, string.Format("Package {0} fetched from {1}", packageId, packageRepo.Id), userId, -1); + Audit(uow, AuditType.PackagerInstall, string.Format("Package {0} fetched from {1}", packageId, Constants.PackageRepository.DefaultRepositoryId), userId, -1); return null; } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c1a102e954..f12ff4f4f1 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -281,8 +281,6 @@ - - @@ -308,10 +306,6 @@ - - - - @@ -1606,6 +1600,9 @@ Constants.cs + + Constants.cs + Constants.cs diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs deleted file mode 100644 index b10a35f5f4..0000000000 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class PackageRepositoriesElementDefaultTests : PackageRepositoriesElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - - [Test] - public override void Repositories() - { - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.Count() == 1); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Id == Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66")); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Name == "Umbraco package Repository"); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementTests.cs deleted file mode 100644 index cb82978c72..0000000000 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class PackageRepositoriesElementTests : UmbracoSettingsTests - { - [Test] - public virtual void Repositories() - { - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.Count() == 2); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Id == Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66")); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Name == "Umbraco package Repository"); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).HasCustomWebServiceUrl == false); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).WebServiceUrl == "/umbraco/webservices/api/repository.asmx"); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).RepositoryUrl == "http://packages.umbraco.org"); - - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(1).Id == Guid.Parse("163245E0-CD22-44B6-841A-1B9B9D2E955F")); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(1).Name == "Test Repo"); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(1).HasCustomWebServiceUrl == false); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).WebServiceUrl == "/umbraco/webservices/api/repository.asmx"); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).RepositoryUrl == "http://packages.umbraco.org"); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index d67a44de46..6c54bc335c 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -38,7 +38,6 @@ namespace Umbraco.Tests.TestHelpers var logging = new Mock(); var tasks = new Mock(); var distCall = new Mock(); - var repos = new Mock(); var providers = new Mock(); var routing = new Mock(); @@ -53,7 +52,6 @@ namespace Umbraco.Tests.TestHelpers settings.Setup(x => x.Logging).Returns(logging.Object); settings.Setup(x => x.ScheduledTasks).Returns(tasks.Object); settings.Setup(x => x.DistributedCall).Returns(distCall.Object); - settings.Setup(x => x.PackageRepositories).Returns(repos.Object); settings.Setup(x => x.Providers).Returns(providers.Object); settings.Setup(x => x.WebRouting).Returns(routing.Object); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 0d707bc56b..d2d94bc694 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -361,8 +361,6 @@ - - diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 1577442ede..fa3ff3ca72 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -49,6 +49,7 @@ true true + bin\ @@ -492,7 +493,6 @@ - @@ -1037,7 +1037,7 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" True 7110 / - http://localhost:7110 + http://localhost:3110 False False diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 31c32d4d19..658eb07f0a 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -279,12 +279,6 @@ - - - - - - @@ -326,4 +320,4 @@ umbracoApplicationUrl=""> - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx b/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx deleted file mode 100644 index a7fe23357d..0000000000 --- a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx +++ /dev/null @@ -1,346 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" - AutoEventWireup="True" Inherits="umbraco.presentation.developer.packages.Installer" - Trace="false" ValidateRequest="false" %> -<%@ Import Namespace="umbraco" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - - - - - - - - -
-

- Only install packages from sources you know and trust!

-

- When installing an Umbraco package you should use the same caution as when you install - an application on your computer.

-

- A malicious package could damage your Umbraco installation just like a malicious - application can damage your computer. -

-

- It is recommended to install from the official Umbraco package - repository or a custom repository whenever it's possible. -

-

- - -

-
-
- - -

- -
- - - <%= umbraco.ui.Text("packager", "chooseLocalPackageText") %> - -

-
- - - - -
- - -
-

- This repository requires authentication before you can download any packages from - it.
- Please enter email and password to login. -

-
-
- - - - - - -
- - - - - -
-

- Please note: Installing a package containing several items and - files can take some time. Do not refresh the page or navigate away before, the installer - notifies you once the install is completed. -

-
- - - - - - - - - - - - - - - - - -
-

Binary files in the package!

- - Read more... -
-

- This package contains .NET code. This is not unusual as .NET code - is used for any advanced functionality on an Umbraco powered website.

-

- However, if you don't know the author of the package or are unsure why this package - contains these files, it is adviced not to continue the installation. -

-

- The Files in question:
-

    - -
-

-
-
- -
- - -
-

- Legacy Property editors detected

- Read more... -
-

- This package contains legacy property editors which are not compatible with Umbraco 7

-

- This package may not function correctly if the package developer has not indicated that - it is compatible with version 7. Any DataTypes this package creates that do not have - a Version 7 compatible property editor will be converted to use a Label/NoEdit property editor. -

-
-
-
- - -
-

- Binary file errors detected

- Read more... -
-

- This package contains .NET binary files that might not be compatible with this version of Umbraco. - If you aren't sure what these errors mean or why they are listed please contact the package creator. -

-

- Error report
-

    - -
-

-
-
-
- -
-

- Macro Conflicts in the package!

- Read more... -
-

- This package contains one or more macros which have the same alias as an existing one on your site, based on the Macro Alias. -

-

- If you choose to continue your existing macros will be replaced with the ones from this package. If you do not want to overwrite your existing macros you will need to change their alias. -

-

- The Macros in question:
-

    - -
-

-
-
-
- - -
-

- Template Conflicts in the package!

- Read more... -
-

- This package contains one or more templates which have the same alias as an existing one on your site, based on the Template Alias. -

-

- If you choose to continue your existing template will be replaced with the ones from this package. If you do not want to overwrite your existing templates you will need to change their alias. -

-

- The Templates in question:
-

    - -
-

-
-
-
- - -
-

- Stylesheet Conflicts in the package!

- Read more... -
-

- This package contains one or more stylesheets which have the same alias as an existing one on your site, based on the Stylesheet Name. -

-

- If you choose to continue your existing stylesheets will be replaced with the ones from this package. If you do not want to overwrite your existing stylesheets you will need to change their name. -

-

- The Stylesheets in question:
-

    - -
-

-
-
-
- - -
- - -
-
- -
- - - - - - - - - - - - -

- All items in the package have been installed

-

- Overview of what was installed can be found under "installed package" in the developer - section.

-

- Uninstall is available at the same location.

-

- - -

- -
-
- - - - -

<%= umbraco.ui.Text("packager", "packageUninstalledText") %>

- -
-
- - - - -
- Please wait while the browser is reloaded... -
- - - -
-
- -
-
diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 6903d9db23..faecec759b 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -20,6 +20,7 @@ using Umbraco.Web.Mvc; using Umbraco.Web.PropertyEditors; using Umbraco.Web.Trees; using Umbraco.Web.WebServices; +using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors { @@ -113,7 +114,7 @@ namespace Umbraco.Web.Editors {"serverVarsJs", _urlHelper.Action("Application", "BackOffice")}, //API URLs { - "packagesRestApiBaseUrl", UmbracoConfig.For.UmbracoSettings().PackageRepositories.GetDefault().RestApiUrl + "packagesRestApiBaseUrl", Constants.PackageRepository.RestApiBaseUrl }, { "redirectUrlManagementApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 6b5f460171..057d97ef33 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -10,9 +10,7 @@ using System.Web.Http; using System.Xml; using umbraco; using umbraco.cms.businesslogic.packager; -using umbraco.cms.businesslogic.packager.repositories; using umbraco.cms.presentation.Trees; -using umbraco.presentation.developer.packages; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Events; diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index b578e20401..6d1140e78f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -532,9 +532,6 @@ - - ASPXCodeBehind - @@ -1717,7 +1714,6 @@ - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadPackager.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadPackager.cs index 5470d530ad..5146b268cc 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadPackager.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadPackager.cs @@ -1,27 +1,7 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.Data; -using System.IO; using System.Text; -using System.Web; -using System.Xml; -using System.Configuration; -using umbraco.BasePages; -using umbraco.BusinessLogic; -using umbraco.businesslogic; -using umbraco.cms.businesslogic; -using umbraco.cms.businesslogic.cache; -using umbraco.cms.businesslogic.contentitem; -using umbraco.cms.businesslogic.datatype; -using umbraco.cms.businesslogic.language; -using umbraco.cms.businesslogic.media; -using umbraco.cms.businesslogic.member; -using umbraco.cms.businesslogic.property; -using umbraco.cms.businesslogic.web; using umbraco.interfaces; -using umbraco.DataLayer; -using umbraco.BusinessLogic.Utils; using Umbraco.Core; using umbraco.cms.presentation.Trees; @@ -34,11 +14,9 @@ namespace umbraco [Obsolete("This is no longer used and will be removed from the codebase in the future")] public class loadPackager : BaseTree { - #region TreeI Members public loadPackager(string application) : base(application) { } protected override void CreateRootNode(ref XmlTreeNode rootNode) { - } private int _id; @@ -65,7 +43,7 @@ namespace umbraco protected override void CreateAllowedActions(ref List actions) { actions.Clear(); - actions.Add(umbraco.BusinessLogic.Actions.ActionRefresh.Instance); + actions.Add(BusinessLogic.Actions.ActionRefresh.Instance); } protected override void CreateRootNodeActions(ref List actions) @@ -93,89 +71,61 @@ namespace umbraco /// /// The tree. public override void Render(ref XmlTree tree) - { + { string[,] items = { { "BrowseRepository.aspx", "Install from repository" }, { "CreatePackage.aspx", "Created Packages" }, { "installedPackages.aspx", "Installed packages" }, { "StarterKits.aspx", "Starter kit" }, { "installer.aspx", "Install local package" } }; - - for (int i = 0; i <= items.GetUpperBound(0); i++) + for (var i = 0; i <= items.GetUpperBound(0); i++) { - XmlTreeNode xNode = XmlTreeNode.Create(this); + var xNode = XmlTreeNode.Create(this); xNode.NodeID = (i + 1).ToInvariantString(); xNode.Text = items[i, 1]; xNode.Icon = "icon-folder"; xNode.OpenIcon = "icon-folder"; - - + //Make sure the different sections load the correct childnodes. switch (items[i, 0]) { case "installedPackages.aspx": - if (cms.businesslogic.packager.InstalledPackage.GetAllInstalledPackages().Count > 0) { - xNode.Source = "tree.aspx?app=" + this._app + "&id=" + this._id + "&treeType=packagerPackages&packageType=installed" + "&rnd=" + Guid.NewGuid(); - xNode.NodeType = "installedPackages"; - xNode.Text = ui.Text("treeHeaders", "installedPackages"); + xNode.Source = $"tree.aspx?app={_app}&id={_id}&treeType=packagerPackages&packageType=installed&rnd={Guid.NewGuid()}"; + xNode.NodeType = "installedPackages"; + xNode.Text = ui.Text("treeHeaders", "installedPackages"); xNode.HasChildren = true; } else { xNode.Text = ""; } - xNode.Action = "javascript:void(0);"; - break; case "BrowseRepository.aspx": - - /* - //Gets all the repositories registered in umbracoSettings.config - var repos = cms.businesslogic.packager.repositories.Repository.getAll(); - - - //if more then one repo, then list them as child nodes under the "Install from repository" node. - // the repositories will then be fetched from the loadPackages class. - if (repos.Count > 1) - { - xNode.Source = "tree.aspx?app=" + this._app + "&id=" + this._id + "&treeType=packagerPackages&packageType=repositories" + "&rnd=" + Guid.NewGuid(); - xNode.NodeType = "packagesRepositories"; - xNode.Text = ui.Text("treeHeaders", "repositories"); - xNode.HasChildren = true; - } - */ - //if only one repo, then just list it directly and name it as the repository. - //the packages will be loaded from the loadPackages class with a repoAlias querystring - var repos = cms.businesslogic.packager.repositories.Repository.getAll(); - - xNode.Text = repos[0].Name; - xNode.Source = "tree.aspx?app=" + this._app + "&id=" + this._id + "&treeType=packagerPackages&packageType=repository&repoGuid=" + repos[0].Guid + "&rnd=" + Guid.NewGuid(); + xNode.Text = Constants.PackageRepository.DefaultRepositoryName; + xNode.Source = $"tree.aspx?app={_app}&id={_id}&treeType=packagerPackages&packageType=repository&repoGuid={Constants.PackageRepository.DefaultRepositoryId}&rnd={Guid.NewGuid()}"; xNode.NodeType = "packagesRepository"; - xNode.Action = "javascript:openPackageCategory('BrowseRepository.aspx?repoGuid=" + repos[0].Guid + "');"; + xNode.Action = $"javascript:openPackageCategory(\'BrowseRepository.aspx?repoGuid={Constants.PackageRepository.DefaultRepositoryId}\');"; xNode.Icon = "icon-server-alt"; xNode.HasChildren = true; - break; - case "CreatePackage.aspx": - xNode.Source = "tree.aspx?app=" + this._app + "&id=" + this._id + "&treeType=packagerPackages&packageType=created" + "&rnd=" + Guid.NewGuid(); + xNode.Source = $"tree.aspx?app={_app}&id={_id}&treeType=packagerPackages&packageType=created&rnd={Guid.NewGuid()}"; xNode.NodeType = "createdPackages"; - xNode.Menu.Clear(); - xNode.Menu.Add(umbraco.BusinessLogic.Actions.ActionNew.Instance); - xNode.Menu.Add(umbraco.BusinessLogic.Actions.ActionRefresh.Instance); + xNode.Menu.Clear(); + xNode.Menu.Add(BusinessLogic.Actions.ActionNew.Instance); + xNode.Menu.Add(BusinessLogic.Actions.ActionRefresh.Instance); xNode.Text = ui.Text("treeHeaders", "createdPackages"); xNode.HasChildren = true; xNode.Action = "javascript:void(0);"; - break; case "installer.aspx": xNode.Source = ""; xNode.NodeType = "uploadPackage"; xNode.Icon = "icon-page-up"; - xNode.Action = "javascript:openPackageCategory('" + items[i, 0] + "');"; + xNode.Action = $"javascript:openPackageCategory(\'{items[i, 0]}\');"; xNode.Text = ui.Text("treeHeaders", "localPackage"); xNode.Menu.Clear(); break; @@ -183,7 +133,7 @@ namespace umbraco case "StarterKits.aspx": xNode.Source = ""; xNode.NodeType = "starterKits"; - xNode.Action = "javascript:openPackageCategory('" + items[i, 0] + "');"; + xNode.Action = $"javascript:openPackageCategory(\'{items[i, 0]}\');"; xNode.Icon = "icon-flash"; xNode.Text = ui.Text("treeHeaders", "installStarterKit"); xNode.Menu.Clear(); @@ -199,7 +149,4 @@ namespace umbraco } } - - #endregion - } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadPackages.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadPackages.cs deleted file mode 100644 index 154f995618..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadPackages.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using System.Xml; -using umbraco.businesslogic; -using umbraco.cms.businesslogic.packager; -using umbraco.cms.presentation.Trees; -using Umbraco.Core; -using umbraco.interfaces; - -namespace umbraco -{ - //[Tree(Constants.Applications.Developer, "packagerPackages", "Packager Packages", initialize: false, sortOrder: 1)] - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - public class loadPackages : BaseTree - { - - public const string PACKAGE_TREE_PREFIX = "package_"; - - public loadPackages(string application) : base(application) { } - - protected override void CreateRootNode(ref XmlTreeNode rootNode) - { - - } - - private int _id; - private string _app; - private string _packageType = ""; - private string _repoGuid = ""; - - public override void RenderJS(ref StringBuilder Javascript) - { - Javascript.Append(@" - function openCreatedPackage(id) { - UmbClientMgr.contentFrame('developer/packages/editPackage.aspx?id=' + id); - } - function openInstalledPackage(id) { - UmbClientMgr.contentFrame('developer/packages/installedPackage.aspx?id=' + id); - } - "); - } - - protected override void CreateAllowedActions(ref List actions) - { - actions.Clear(); - } - - public override void Render(ref XmlTree tree) - { - - _packageType = HttpContext.Current.Request.QueryString["packageType"]; - - switch (_packageType) - { - case "installed": - Version v; - // Display the unique packages, ordered by the latest version number. [LK 2013-06-10] - var uniquePackages = InstalledPackage.GetAllInstalledPackages() - .OrderByDescending(x => Version.TryParse(x.Data.Version, out v) ? v : new Version()) - .GroupBy(x => x.Data.Name) - .Select(x => x.First()) - .OrderBy(x => x.Data.Name); - foreach (var p in uniquePackages) - { - var xNode = XmlTreeNode.Create(this); - xNode.NodeID = string.Concat(PACKAGE_TREE_PREFIX, p.Data.Id); - xNode.Text = p.Data.Name; - xNode.Action = string.Format("javascript:openInstalledPackage('{0}');", p.Data.Id); - xNode.Icon = "icon-box"; - xNode.OpenIcon = "icon-box"; - xNode.NodeType = "createdPackageInstance"; - tree.Add(xNode); - } - break; - - case "created": - foreach (CreatedPackage p in CreatedPackage.GetAllCreatedPackages()) - { - - XmlTreeNode xNode = XmlTreeNode.Create(this); - xNode.NodeID = PACKAGE_TREE_PREFIX + p.Data.Id.ToString(); - xNode.Text = p.Data.Name; - xNode.Action = "javascript:openCreatedPackage('" + p.Data.Id.ToString() + "');"; - xNode.Icon = "icon-box"; - xNode.OpenIcon = "icon-box"; - xNode.NodeType = "createdPackageInstance"; - xNode.Menu.Add(umbraco.BusinessLogic.Actions.ActionDelete.Instance); - tree.Add(xNode); - } - break; - - case "repositories": - List repos = cms.businesslogic.packager.repositories.Repository.getAll(); - - foreach (cms.businesslogic.packager.repositories.Repository repo in repos) - { - XmlTreeNode xNode = XmlTreeNode.Create(this); - xNode.Text = repo.Name; - xNode.Action = "javascript:openPackageCategory('BrowseRepository.aspx?repoGuid=" + repo.Guid + "');"; - xNode.Icon = "icon-server-alt"; - xNode.OpenIcon = "icon-server-alt"; - xNode.NodeType = "packagesRepo" + repo.Guid; - xNode.Menu.Add( umbraco.BusinessLogic.Actions.ActionRefresh.Instance ); - xNode.Source = "tree.aspx?app=" + this._app + "&id=" + this._id + "&treeType=packagerPackages&packageType=repository&repoGuid=" + repo.Guid + "&rnd=" + Guid.NewGuid(); - tree.Add(xNode); - - } - - break; - case "repository": - - _repoGuid = HttpContext.Current.Request.QueryString["repoGuid"]; - Umbraco.Web.org.umbraco.our.Repository r = new Umbraco.Web.org.umbraco.our.Repository(); - - foreach (var cat in r.Categories(_repoGuid)) - { - XmlTreeNode xNode = XmlTreeNode.Create(this); - xNode.NodeID = cat.Id.ToInvariantString(); - xNode.Text = cat.Text; - xNode.Action = "javascript:openPackageCategory('BrowseRepository.aspx?category=" + cat.Id + "&repoGuid=" + _repoGuid + "');"; - xNode.Icon = "icon-folder"; - xNode.OpenIcon = "icon-folder"; - xNode.NodeType = "packagesCategory" + cat.Id; - tree.Add(xNode); - - } - - break; - } - - } - - - } - -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs index 8c2f9ca837..de8639f7ac 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs @@ -27,7 +27,7 @@ namespace umbraco // we need to grab the id from the alias as the new tree needs to prefix the NodeID with "package_" if (ParentID == 0) { - ParentID = int.Parse(Alias.Substring(loadPackages.PACKAGE_TREE_PREFIX.Length)); + ParentID = int.Parse(Alias.Substring("package_".Length)); } cms.businesslogic.packager.CreatedPackage.GetById(ParentID).Delete(); return true; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs deleted file mode 100644 index a9bf7b5e43..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs +++ /dev/null @@ -1,835 +0,0 @@ -using System; -using System.Collections; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Globalization; -using System.Threading; -using System.Web; -using System.Web.SessionState; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; -using System.Xml; -using System.Xml.XPath; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Web; -using umbraco.BasePages; -using umbraco.BusinessLogic; -using umbraco.cms.presentation.Trees; -using Umbraco.Core; -using BizLogicAction = umbraco.BusinessLogic.Actions.Action; - -namespace umbraco.presentation.developer.packages -{ - /// - /// Summary description for packager. - /// - [Obsolete("This should not be used and will be removed in v8, this is kept here only for backwards compat reasons, this page should never be rendered/used")] - public class Installer : UmbracoEnsuredPage - { - public Installer() - { - CurrentApp = DefaultApps.developer.ToString(); - _installer = new cms.businesslogic.packager.Installer(UmbracoUser.Id); - } - - private Control _configControl; - private cms.businesslogic.packager.repositories.Repository _repo; - private readonly cms.businesslogic.packager.Installer _installer = null; - private string _tempFileName = ""; - - protected string RefreshQueryString { get; set; } - - protected void Page_Load(object sender, EventArgs e) - { - var ex = new Exception(); - if (!cms.businesslogic.packager.Settings.HasFileAccess(ref ex)) - { - fb.Style.Add("margin-top", "7px"); - fb.type = uicontrols.Feedback.feedbacktype.error; - fb.Text = "" + ui.Text("errors", "filePermissionsError") + ":
" + ex.Message; - } - - if (!IsPostBack) - { - ButtonInstall.Attributes.Add("onClick", "jQuery(this).hide(); jQuery('#installingMessage').show();; return true;"); - ButtonLoadPackage.Attributes.Add("onClick", "jQuery(this).hide(); jQuery('#loadingbar').show();; return true;"); - } - - //if we are actually in the middle of installing something... meaning we keep redirecting back to this page with - // custom query strings - // TODO: SD: This process needs to be fixed/changed/etc... to use the InstallPackageController - // http://issues.umbraco.org/issue/U4-1047 - if (!string.IsNullOrEmpty(Request.GetItemAsString("installing"))) - { - HideAllPanes(); - pane_installing.Visible = true; - ProcessInstall(Request.GetItemAsString("installing")); //process the current step - - } - else if (tempFile.Value.IsNullOrWhiteSpace() //if we haven't downloaded the .umb temp file yet - && (!Request.GetItemAsString("guid").IsNullOrWhiteSpace() && !Request.GetItemAsString("repoGuid").IsNullOrWhiteSpace())) - { - //we'll fetch the local information we have about our repo, to find out what webservice to query. - _repo = cms.businesslogic.packager.repositories.Repository.getByGuid(Request.GetItemAsString("repoGuid")); - - if (_repo != null && _repo.HasConnection()) - { - //from the webservice we'll fetch some info about the package. - cms.businesslogic.packager.repositories.Package pack = _repo.Webservice.PackageByGuid(Request.GetItemAsString("guid")); - - //if the package is protected we will ask for the users credentials. (this happens every time they try to fetch anything) - if (!pack.Protected) - { - //if it isn't then go straigt to the accept licens screen - tempFile.Value = _installer.Import(_repo.fetch(Request.GetItemAsString("guid"), UmbracoUser.Id)); - UpdateSettings(); - - } - else if (!IsPostBack) - { - - //Authenticate against the repo - HideAllPanes(); - pane_authenticate.Visible = true; - - } - } - else - { - fb.Style.Add("margin-top", "7px"); - fb.type = uicontrols.Feedback.feedbacktype.error; - fb.Text = "No connection to repository. Runway could not be installed as there was no connection to: '" + _repo.RepositoryUrl + "'"; - pane_upload.Visible = false; - } - } - } - - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender(e); - acceptCheckbox.Attributes.Add("onmouseup", "document.getElementById('" + ButtonInstall.ClientID + "').disabled = false;"); - } - - protected void uploadFile(object sender, EventArgs e) - { - try - { - _tempFileName = Guid.NewGuid().ToString() + ".umb"; - string fileName = SystemDirectories.Data + System.IO.Path.DirectorySeparatorChar + _tempFileName; - file1.PostedFile.SaveAs(IOHelper.MapPath(fileName)); - tempFile.Value = _installer.Import(_tempFileName); - UpdateSettings(); - } - catch (Exception ex) - { - fb.type = global::umbraco.uicontrols.Feedback.feedbacktype.error; - fb.Text = "Could not upload file
" + ex.ToString(); - } - } - - //this fetches the protected package from the repo. - protected void fetchProtectedPackage(object sender, EventArgs e) - { - //we auth against the webservice. This key will be used to fetch the protected package. - string memberGuid = _repo.Webservice.authenticate(tb_email.Text, library.CreateHash(tb_password.Text)); - - //if we auth correctly and get a valid key back, we will fetch the file from the repo webservice. - if (string.IsNullOrEmpty(memberGuid) == false) - { - tempFile.Value = _installer.Import(_repo.fetch(helper.Request("guid"), memberGuid)); - UpdateSettings(); - } - } - - //this loads the accept license screen - private void UpdateSettings() - { - HideAllPanes(); - - pane_acceptLicense.Visible = true; - pane_acceptLicenseInner.Text = "Installing the package: " + _installer.Name; - Panel1.Text = "Installing the package: " + _installer.Name; - - - if (_installer.ContainsUnsecureFiles) - { - pp_unsecureFiles.Visible = true; - foreach (string str in _installer.UnsecureFiles) - { - lt_files.Text += "
  • " + str + "
  • "; - } - } - - if (_installer.ContainsLegacyPropertyEditors) - { - LegacyPropertyEditorPanel.Visible = true; - } - - if (_installer.ContainsBinaryFileErrors) - { - BinaryFileErrorsPanel.Visible = true; - foreach (var str in _installer.BinaryFileErrors) - { - BinaryFileErrorReport.Text += "
  • " + str + "
  • "; - } - } - - if (_installer.ContainsMacroConflict) - { - pp_macroConflicts.Visible = true; - foreach (var item in _installer.ConflictingMacroAliases) - { - ltrMacroAlias.Text += "
  • " + item.Key + " (Alias: " + item.Value + ")
  • "; - } - } - - if (_installer.ContainsTemplateConflicts) - { - pp_templateConflicts.Visible = true; - foreach (var item in _installer.ConflictingTemplateAliases) - { - ltrTemplateAlias.Text += "
  • " + item.Key + " (Alias: " + item.Value + ")
  • "; - } - } - - if (_installer.ContainsStyleSheeConflicts) - { - pp_stylesheetConflicts.Visible = true; - foreach (var item in _installer.ConflictingStyleSheetNames) - { - ltrStylesheetNames.Text += "
  • " + item.Key + " (Alias: " + item.Value + ")
  • "; - } - } - - LabelName.Text = _installer.Name + " Version: " + _installer.Version; - LabelMore.Text = "" + _installer.Url + ""; - LabelAuthor.Text = "" + _installer.Author + ""; - LabelLicense.Text = "" + _installer.License + ""; - - if (_installer.ReadMe != "") - readme.Text = "
    " + library.ReplaceLineBreaks(library.StripHtml(_installer.ReadMe)) + "
    "; - else - readme.Text = "No information
    "; - } - - - private void ProcessInstall(string currentStep) - { - var dir = Request.GetItemAsString("dir"); - var packageId = 0; - int.TryParse(Request.GetItemAsString("pId"), out packageId); - - switch (currentStep.ToLowerInvariant()) - { - case "businesslogic": - //first load in the config from the temporary directory - //this will ensure that the installer have access to all the new files and the package manifest - _installer.LoadConfig(dir); - _installer.InstallBusinessLogic(packageId, dir); - - - //making sure that publishing actions performed from the cms layer gets pushed to the presentation - library.RefreshContent(); - - if (string.IsNullOrEmpty(_installer.Control) == false) - { - Response.Redirect("installer.aspx?installing=refresh&dir=" + dir + "&pId=" + packageId.ToString() + "&customControl=" + Server.UrlEncode(_installer.Control) + "&customUrl=" + Server.UrlEncode(_installer.Url)); - } - else - { - Response.Redirect("installer.aspx?installing=refresh&dir=" + dir + "&pId=" + packageId.ToString() + "&customUrl=" + Server.UrlEncode(_installer.Url)); - } - break; - case "custominstaller": - var customControl = Request.GetItemAsString("customControl"); - - if (customControl.IsNullOrWhiteSpace() == false) - { - HideAllPanes(); - - _configControl = LoadControl(SystemDirectories.Root + customControl); - _configControl.ID = "packagerConfigControl"; - - pane_optional.Controls.Add(_configControl); - pane_optional.Visible = true; - - if (!IsPostBack) - { - //We still need to clean everything up which is normally done in the Finished Action - PerformPostInstallCleanup(packageId, dir); - } - - } - else - { - //if the custom installer control is empty here (though it should never be because we've already checked for it previously) - //then we should run the normal FinishedAction - PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); - } - break; - case "refresh": - PerformRefreshAction(packageId, dir, Request.GetItemAsString("customUrl"), Request.GetItemAsString("customControl")); - break; - case "finished": - PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); - break; - case "uninstalled": - PerformUninstalledAction(); - break; - default: - break; - } - } - - /// - /// Perform the 'Finished' action of the installer - /// - /// - /// - /// - private void PerformFinishedAction(int packageId, string dir, string url) - { - HideAllPanes(); - //string url = _installer.Url; - string packageViewUrl = "installedPackage.aspx?id=" + packageId.ToString(CultureInfo.InvariantCulture); - - bt_viewInstalledPackage.OnClientClick = "document.location = '" + packageViewUrl + "'; return false;"; - - if (!string.IsNullOrEmpty(url)) - lit_authorUrl.Text = " " + ui.Text("or") + " " + ui.Text("viewPackageWebsite") + ""; - - - pane_success.Visible = true; - - PerformPostInstallCleanup(packageId, dir); - } - - private void PerformUninstalledAction() - { - HideAllPanes(); - Panel1.Text = "Package has been uninstalled"; - pane_uninstalled.Visible = true; - } - - /// - /// Perform the 'Refresh' action of the installer - /// - /// - /// - /// - /// - private void PerformRefreshAction(int packageId, string dir, string url, string customControl) - { - HideAllPanes(); - - //create the URL to refresh to - // /umbraco/developer/packages/installer.aspx?installing=finished - // &dir=X:\Projects\Umbraco\Umbraco_7.0\src\Umbraco.Web.UI\App_Data\aef8c41f-63a0-494b-a1e2-10d761647033 - // &pId=3 - // &customUrl=http:%2f%2four.umbraco.org%2fprojects%2fwebsite-utilities%2fmerchello - - if (customControl.IsNullOrWhiteSpace()) - { - RefreshQueryString = Server.UrlEncode(string.Format( - "installing=finished&dir={0}&pId={1}&customUrl={2}", - dir, packageId, url)); - } - else - { - RefreshQueryString = Server.UrlEncode(string.Format( - "installing=customInstaller&dir={0}&pId={1}&customUrl={2}&customControl={3}", - dir, packageId, url, customControl)); - } - - pane_refresh.Visible = true; - - PerformPostInstallCleanup(packageId, dir); - } - - /// - /// Runs Post refresh actions such reloading the correct tree nodes, etc... - /// - private void PerformPostRefreshAction() - { - BasePage.Current.ClientTools.ReloadActionNode(true, true); - } - - /// - /// Runs Post install actions such as clearning any necessary cache, reloading the correct tree nodes, etc... - /// - /// - /// - private void PerformPostInstallCleanup(int packageId, string dir) - { - _installer.InstallCleanUp(packageId, dir); - - // Update ClientDependency version - var clientDependencyConfig = new Umbraco.Core.Configuration.ClientDependencyConfiguration(LoggerResolver.Current.Logger); - var clientDependencyUpdated = clientDependencyConfig.IncreaseVersionNumber(); - - //clear the tree cache - we'll do this here even though the browser will reload, but just in case it doesn't can't hurt. - ClientTools.ClearClientTreeCache().RefreshTree("packager"); - TreeDefinitionCollection.Instance.ReRegisterTrees(); - BizLogicAction.ReRegisterActionsAndHandlers(); - } - - //this accepts the package, creates the manifest and then installs the files. - protected void startInstall(object sender, System.EventArgs e) - { - //we will now create the installer manifest, which means that umbraco can register everything that gets added to the system - //this returns an id of the manifest. - - _installer.LoadConfig(tempFile.Value); - - int pId = _installer.CreateManifest(tempFile.Value, helper.Request("guid"), helper.Request("repoGuid")); - - //and then copy over the files. This will take some time if it contains .dlls that will reboot the system.. - _installer.InstallFiles(pId, tempFile.Value); - - //TODO: This is a total hack, we need to refactor the installer to be just like the package installer during the - // install process and use AJAX to ensure that app pool restarts and restarts PROPERLY before installing the business - // logic. Until then, we are going to put a thread sleep here for 2 seconds in hopes that we always fluke out and the app - // pool will be restarted after redirect. - Thread.Sleep(2000); - - Response.Redirect("installer.aspx?installing=businesslogic&dir=" + tempFile.Value + "&pId=" + pId.ToString()); - } - - private void HideAllPanes() - { - pane_authenticate.Visible = false; - pane_acceptLicense.Visible = false; - pane_installing.Visible = false; - pane_optional.Visible = false; - pane_success.Visible = false; - pane_refresh.Visible = false; - pane_upload.Visible = false; - } - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.UmbracoPanel Panel1; - - /// - /// fb control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Feedback fb; - - /// - /// pane_upload control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_upload; - - /// - /// PropertyPanel9 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel9; - - /// - /// file1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputFile file1; - - /// - /// ButtonLoadPackage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button ButtonLoadPackage; - - /// - /// progbar1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar progbar1; - - /// - /// pane_authenticate control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_authenticate; - - /// - /// tb_email control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_email; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; - - /// - /// tb_password control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_password; - - /// - /// PropertyPanel2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; - - /// - /// Button1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button Button1; - - /// - /// pane_acceptLicense control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel pane_acceptLicense; - - /// - /// pane_acceptLicenseInner control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_acceptLicenseInner; - - /// - /// PropertyPanel3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; - - /// - /// LabelName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelName; - - /// - /// PropertyPanel5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel5; - - /// - /// LabelAuthor control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelAuthor; - - /// - /// PropertyPanel4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel4; - - /// - /// LabelMore control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelMore; - - /// - /// PropertyPanel6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel6; - - /// - /// LabelLicense control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelLicense; - - /// - /// PropertyPanel7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel7; - - /// - /// acceptCheckbox control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox acceptCheckbox; - - /// - /// PropertyPanel8 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel8; - - /// - /// readme control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal readme; - - /// - /// pp_unsecureFiles control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_unsecureFiles; - - /// - /// lt_files control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lt_files; - - /// - /// pp_macroConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_macroConflicts; - - /// - /// ltrMacroAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrMacroAlias; - - /// - /// pp_templateConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_templateConflicts; - - protected global::umbraco.uicontrols.PropertyPanel BinaryFileErrorsPanel; - protected global::umbraco.uicontrols.PropertyPanel LegacyPropertyEditorPanel; - protected global::System.Web.UI.WebControls.Literal BinaryFileErrorReport; - - /// - /// ltrTemplateAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrTemplateAlias; - - /// - /// pp_stylesheetConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_stylesheetConflicts; - - /// - /// ltrStylesheetNames control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrStylesheetNames; - - /// - /// _progbar1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar _progbar1; - - /// - /// ButtonInstall control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button ButtonInstall; - - /// - /// pane_installing control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_installing; - - protected global::umbraco.uicontrols.Pane pane_uninstalled; - - - /// - /// progBar2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar progBar2; - - /// - /// lit_installStatus control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lit_installStatus; - - /// - /// pane_optional control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_optional; - - /// - /// pane_success control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_success; - - protected global::umbraco.uicontrols.Pane pane_refresh; - - /// - /// bt_viewInstalledPackage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_viewInstalledPackage; - - /// - /// lit_authorUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lit_authorUrl; - - /// - /// tempFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; - - /// - /// processState control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden processState; - } -} diff --git a/src/umbraco.cms/businesslogic/Packager/Repositories/Repository.cs b/src/umbraco.cms/businesslogic/Packager/Repositories/Repository.cs deleted file mode 100644 index 8609504313..0000000000 --- a/src/umbraco.cms/businesslogic/Packager/Repositories/Repository.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml; -using System.IO; -using System.Net; -using Umbraco.Core; -using Umbraco.Core.Auditing; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.IO; - -namespace umbraco.cms.businesslogic.packager.repositories -{ - [Obsolete("This should not be used and will be removed in future Umbraco versions")] - public class Repository : DisposableObjectSlim - { - public string Guid { get; private set; } - - public string Name { get; private set; } - - public string RepositoryUrl { get; private set; } - - public string WebserviceUrl { get; private set; } - - - public RepositoryWebservice Webservice - { - get - { - var repo = new RepositoryWebservice(WebserviceUrl); - return repo; - } - } - - public SubmitStatus SubmitPackage(string authorGuid, PackageInstance package, byte[] doc) - { - - string packageName = package.Name; - string packageGuid = package.PackageGuid; - string description = package.Readme; - string packageFile = package.PackagePath; - - - System.IO.FileStream fs1 = null; - - try - { - - byte[] pack = new byte[0]; - fs1 = System.IO.File.Open(IOHelper.MapPath(packageFile), FileMode.Open, FileAccess.Read); - pack = new byte[fs1.Length]; - fs1.Read(pack, 0, (int) fs1.Length); - fs1.Close(); - fs1 = null; - - byte[] thumb = new byte[0]; //todo upload thumbnail... - - return Webservice.SubmitPackage(Guid, authorGuid, packageGuid, pack, doc, thumb, packageName, "", "", description); - } - catch (Exception ex) - { - LogHelper.Error("An error occurred in SubmitPackage", ex); - - return SubmitStatus.Error; - } - } - - public static List getAll() - { - - var repositories = new List(); - - foreach (var r in UmbracoConfig.For.UmbracoSettings().PackageRepositories.Repositories) - { - var repository = new Repository - { - Guid = r.Id.ToString(), - Name = r.Name - }; - - repository.RepositoryUrl = r.RepositoryUrl; - repository.WebserviceUrl = repository.RepositoryUrl.Trim('/') + "/" + r.WebServiceUrl.Trim('/'); - if (r.HasCustomWebServiceUrl) - { - string wsUrl = r.WebServiceUrl; - - if (wsUrl.Contains("://")) - { - repository.WebserviceUrl = r.WebServiceUrl; - } - else - { - repository.WebserviceUrl = repository.RepositoryUrl.Trim('/') + "/" + wsUrl.Trim('/'); - } - } - - repositories.Add(repository); - } - - return repositories; - } - - public static Repository getByGuid(string repositoryGuid) - { - Guid id; - if (System.Guid.TryParse(repositoryGuid, out id) == false) - { - throw new FormatException("The repositoryGuid is not a valid GUID"); - } - - var found = UmbracoConfig.For.UmbracoSettings().PackageRepositories.Repositories.FirstOrDefault(x => x.Id == id); - if (found == null) - { - return null; - } - - var repository = new Repository - { - Guid = found.Id.ToString(), - Name = found.Name - }; - - repository.RepositoryUrl = found.RepositoryUrl; - repository.WebserviceUrl = repository.RepositoryUrl.Trim('/') + "/" + found.WebServiceUrl.Trim('/'); - - if (found.HasCustomWebServiceUrl) - { - string wsUrl = found.WebServiceUrl; - - if (wsUrl.Contains("://")) - { - repository.WebserviceUrl = found.WebServiceUrl; - } - else - { - repository.WebserviceUrl = repository.RepositoryUrl.Trim('/') + "/" + wsUrl.Trim('/'); - } - } - - return repository; - } - - - - //shortcut method to download pack from repo and place it on the server... - public string fetch(string packageGuid) - { - return fetch(packageGuid, string.Empty); - } - - public string fetch(string packageGuid, int userId) - { - // log - Audit.Add(AuditTypes.PackagerInstall, - string.Format("Package {0} fetched from {1}", packageGuid, this.Guid), - userId, -1); - return fetch(packageGuid); - } - - /// - /// Used to get the correct package file from the repo for the current umbraco version - /// - /// - /// - /// - /// - public string GetPackageFile(string packageGuid, int userId, System.Version currentUmbracoVersion) - { - // log - Audit.Add(AuditTypes.PackagerInstall, - string.Format("Package {0} fetched from {1}", packageGuid, this.Guid), - userId, -1); - - var fileByteArray = Webservice.GetPackageFile(packageGuid, currentUmbracoVersion.ToString(3)); - - //successfull - if (fileByteArray.Length > 0) - { - // Check for package directory - if (Directory.Exists(IOHelper.MapPath(Settings.PackagerRoot)) == false) - Directory.CreateDirectory(IOHelper.MapPath(Settings.PackagerRoot)); - - using (var fs1 = new FileStream(IOHelper.MapPath(Settings.PackagerRoot + Path.DirectorySeparatorChar + packageGuid + ".umb"), FileMode.Create)) - { - fs1.Write(fileByteArray, 0, fileByteArray.Length); - fs1.Close(); - return "packages\\" + packageGuid + ".umb"; - } - } - - return ""; - } - - public bool HasConnection() - { - - string strServer = this.RepositoryUrl; - - try - { - - HttpWebRequest reqFP = (HttpWebRequest) HttpWebRequest.Create(strServer); - HttpWebResponse rspFP = (HttpWebResponse) reqFP.GetResponse(); - - if (HttpStatusCode.OK == rspFP.StatusCode) - { - - // HTTP = 200 - Internet connection available, server online - rspFP.Close(); - - return true; - - } - else - { - - // Other status - Server or connection not available - - rspFP.Close(); - - return false; - - } - - } - catch (WebException) - { - - // Exception - connection not available - - return false; - - } - } - - - /// - /// This goes and fetches the Byte array for the package from OUR, but it's pretty strange - /// - /// - /// The package ID for the package file to be returned - /// - /// - /// This is a strange Umbraco version parameter - but it's not really an umbraco version, it's a special/odd version format like Version41 - /// but it's actually not used for the 7.5+ package installs so it's obsolete/unused. - /// - /// - public string fetch(string packageGuid, string key) - { - - byte[] fileByteArray = new byte[0]; - - if (key == string.Empty) - { - //SD: this is odd, not sure why it returns a different package depending on the legacy xml schema but I'll leave it for now - if (UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema) - fileByteArray = Webservice.fetchPackage(packageGuid); - else - { - fileByteArray = Webservice.fetchPackageByVersion(packageGuid, Version.Version41); - } - } - else - { - fileByteArray = Webservice.fetchProtectedPackage(packageGuid, key); - } - - //successfull - if (fileByteArray.Length > 0) - { - - // Check for package directory - if (Directory.Exists(IOHelper.MapPath(Settings.PackagerRoot)) == false) - Directory.CreateDirectory(IOHelper.MapPath(Settings.PackagerRoot)); - - using (var fs1 = new FileStream(IOHelper.MapPath(Settings.PackagerRoot + Path.DirectorySeparatorChar + packageGuid + ".umb"), FileMode.Create)) - { - fs1.Write(fileByteArray, 0, fileByteArray.Length); - fs1.Close(); - return "packages\\" + packageGuid + ".umb"; - } - } - - // log - - return ""; - } - - /// - /// Handles the disposal of resources. Derived from abstract class which handles common required locking logic. - /// - protected override void DisposeResources() - { - Webservice.Dispose(); - } - } -} diff --git a/src/umbraco.cms/businesslogic/Packager/Repositories/RepositoryWebservice.cs b/src/umbraco.cms/businesslogic/Packager/Repositories/RepositoryWebservice.cs deleted file mode 100644 index a2da17dd76..0000000000 --- a/src/umbraco.cms/businesslogic/Packager/Repositories/RepositoryWebservice.cs +++ /dev/null @@ -1,2002 +0,0 @@ -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Web.Services; -using System.Web.Services.Protocols; -using System.Xml.Serialization; - -namespace umbraco.cms.businesslogic.packager.repositories -{ - - - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Web.Services.WebServiceBindingAttribute(Name = "RepositorySoap", Namespace = "http://packages.umbraco.org/webservices/")] - public partial class RepositoryWebservice : System.Web.Services.Protocols.SoapHttpClientProtocol - { - - private System.Threading.SendOrPostCallback CategoriesOperationCompleted; - - private System.Threading.SendOrPostCallback NitrosOperationCompleted; - - private System.Threading.SendOrPostCallback NitrosByVersionOperationCompleted; - - private System.Threading.SendOrPostCallback NitrosCategorizedOperationCompleted; - - private System.Threading.SendOrPostCallback NitrosCategorizedByVersionOperationCompleted; - - private System.Threading.SendOrPostCallback StarterKitsOperationCompleted; - - private System.Threading.SendOrPostCallback StarterKitModulesCategorizedOperationCompleted; - - private System.Threading.SendOrPostCallback StarterKitModulesOperationCompleted; - - private System.Threading.SendOrPostCallback authenticateOperationCompleted; - - private System.Threading.SendOrPostCallback GetPackageFileOperationCompleted; - - private System.Threading.SendOrPostCallback fetchPackageByVersionOperationCompleted; - - private System.Threading.SendOrPostCallback fetchPackageOperationCompleted; - - private System.Threading.SendOrPostCallback fetchProtectedPackageOperationCompleted; - - private System.Threading.SendOrPostCallback SubmitPackageOperationCompleted; - - private System.Threading.SendOrPostCallback PackageByGuidOperationCompleted; - - private System.Threading.SendOrPostCallback SkinByGuidOperationCompleted; - - private System.Threading.SendOrPostCallback SkinsOperationCompleted; - - /// - public RepositoryWebservice(string url) - { - this.Url = url;//"http://our.umbraco.org/umbraco/webservices/api/repository.asmx"; - } - - /// - public event CategoriesCompletedEventHandler CategoriesCompleted; - - /// - public event NitrosCompletedEventHandler NitrosCompleted; - - /// - public event NitrosByVersionCompletedEventHandler NitrosByVersionCompleted; - - /// - public event NitrosCategorizedCompletedEventHandler NitrosCategorizedCompleted; - - /// - public event NitrosCategorizedByVersionCompletedEventHandler NitrosCategorizedByVersionCompleted; - - /// - public event StarterKitsCompletedEventHandler StarterKitsCompleted; - - /// - public event StarterKitModulesCategorizedCompletedEventHandler StarterKitModulesCategorizedCompleted; - - /// - public event StarterKitModulesCompletedEventHandler StarterKitModulesCompleted; - - /// - public event authenticateCompletedEventHandler authenticateCompleted; - - /// - public event GetPackageFileCompletedEventHandler GetPackageFileCompleted; - - /// - public event fetchPackageByVersionCompletedEventHandler fetchPackageByVersionCompleted; - - /// - public event fetchPackageCompletedEventHandler fetchPackageCompleted; - - /// - public event fetchProtectedPackageCompletedEventHandler fetchProtectedPackageCompleted; - - /// - public event SubmitPackageCompletedEventHandler SubmitPackageCompleted; - - /// - public event PackageByGuidCompletedEventHandler PackageByGuidCompleted; - - /// - public event SkinByGuidCompletedEventHandler SkinByGuidCompleted; - - /// - public event SkinsCompletedEventHandler SkinsCompleted; - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/Categories", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Category[] Categories(string repositoryGuid) - { - object[] results = this.Invoke("Categories", new object[] { - repositoryGuid}); - return ((Category[])(results[0])); - } - - /// - public System.IAsyncResult BeginCategories(string repositoryGuid, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("Categories", new object[] { - repositoryGuid}, callback, asyncState); - } - - /// - public Category[] EndCategories(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Category[])(results[0])); - } - - /// - public void CategoriesAsync(string repositoryGuid) - { - this.CategoriesAsync(repositoryGuid, null); - } - - /// - public void CategoriesAsync(string repositoryGuid, object userState) - { - if ((this.CategoriesOperationCompleted == null)) - { - this.CategoriesOperationCompleted = new System.Threading.SendOrPostCallback(this.OnCategoriesOperationCompleted); - } - this.InvokeAsync("Categories", new object[] { - repositoryGuid}, this.CategoriesOperationCompleted, userState); - } - - private void OnCategoriesOperationCompleted(object arg) - { - if ((this.CategoriesCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.CategoriesCompleted(this, new CategoriesCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/Nitros", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Package[] Nitros() - { - object[] results = this.Invoke("Nitros", new object[0]); - return ((Package[])(results[0])); - } - - /// - public System.IAsyncResult BeginNitros(System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("Nitros", new object[0], callback, asyncState); - } - - /// - public Package[] EndNitros(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Package[])(results[0])); - } - - /// - public void NitrosAsync() - { - this.NitrosAsync(null); - } - - /// - public void NitrosAsync(object userState) - { - if ((this.NitrosOperationCompleted == null)) - { - this.NitrosOperationCompleted = new System.Threading.SendOrPostCallback(this.OnNitrosOperationCompleted); - } - this.InvokeAsync("Nitros", new object[0], this.NitrosOperationCompleted, userState); - } - - private void OnNitrosOperationCompleted(object arg) - { - if ((this.NitrosCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.NitrosCompleted(this, new NitrosCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/NitrosByVersion", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Package[] NitrosByVersion(Version version) - { - object[] results = this.Invoke("NitrosByVersion", new object[] { - version}); - return ((Package[])(results[0])); - } - - /// - public System.IAsyncResult BeginNitrosByVersion(Version version, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("NitrosByVersion", new object[] { - version}, callback, asyncState); - } - - /// - public Package[] EndNitrosByVersion(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Package[])(results[0])); - } - - /// - public void NitrosByVersionAsync(Version version) - { - this.NitrosByVersionAsync(version, null); - } - - /// - public void NitrosByVersionAsync(Version version, object userState) - { - if ((this.NitrosByVersionOperationCompleted == null)) - { - this.NitrosByVersionOperationCompleted = new System.Threading.SendOrPostCallback(this.OnNitrosByVersionOperationCompleted); - } - this.InvokeAsync("NitrosByVersion", new object[] { - version}, this.NitrosByVersionOperationCompleted, userState); - } - - private void OnNitrosByVersionOperationCompleted(object arg) - { - if ((this.NitrosByVersionCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.NitrosByVersionCompleted(this, new NitrosByVersionCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/NitrosCategorized", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Category[] NitrosCategorized() - { - object[] results = this.Invoke("NitrosCategorized", new object[0]); - return ((Category[])(results[0])); - } - - /// - public System.IAsyncResult BeginNitrosCategorized(System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("NitrosCategorized", new object[0], callback, asyncState); - } - - /// - public Category[] EndNitrosCategorized(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Category[])(results[0])); - } - - /// - public void NitrosCategorizedAsync() - { - this.NitrosCategorizedAsync(null); - } - - /// - public void NitrosCategorizedAsync(object userState) - { - if ((this.NitrosCategorizedOperationCompleted == null)) - { - this.NitrosCategorizedOperationCompleted = new System.Threading.SendOrPostCallback(this.OnNitrosCategorizedOperationCompleted); - } - this.InvokeAsync("NitrosCategorized", new object[0], this.NitrosCategorizedOperationCompleted, userState); - } - - private void OnNitrosCategorizedOperationCompleted(object arg) - { - if ((this.NitrosCategorizedCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.NitrosCategorizedCompleted(this, new NitrosCategorizedCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/NitrosCategorizedByVersion", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Category[] NitrosCategorizedByVersion(Version version) - { - object[] results = this.Invoke("NitrosCategorizedByVersion", new object[] { - version}); - return ((Category[])(results[0])); - } - - /// - public System.IAsyncResult BeginNitrosCategorizedByVersion(Version version, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("NitrosCategorizedByVersion", new object[] { - version}, callback, asyncState); - } - - /// - public Category[] EndNitrosCategorizedByVersion(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Category[])(results[0])); - } - - /// - public void NitrosCategorizedByVersionAsync(Version version) - { - this.NitrosCategorizedByVersionAsync(version, null); - } - - /// - public void NitrosCategorizedByVersionAsync(Version version, object userState) - { - if ((this.NitrosCategorizedByVersionOperationCompleted == null)) - { - this.NitrosCategorizedByVersionOperationCompleted = new System.Threading.SendOrPostCallback(this.OnNitrosCategorizedByVersionOperationCompleted); - } - this.InvokeAsync("NitrosCategorizedByVersion", new object[] { - version}, this.NitrosCategorizedByVersionOperationCompleted, userState); - } - - private void OnNitrosCategorizedByVersionOperationCompleted(object arg) - { - if ((this.NitrosCategorizedByVersionCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.NitrosCategorizedByVersionCompleted(this, new NitrosCategorizedByVersionCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/StarterKits", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Package[] StarterKits() - { - object[] results = this.Invoke("StarterKits", new object[0]); - return ((Package[])(results[0])); - } - - /// - public System.IAsyncResult BeginStarterKits(System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("StarterKits", new object[0], callback, asyncState); - } - - /// - public Package[] EndStarterKits(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Package[])(results[0])); - } - - /// - public void StarterKitsAsync() - { - this.StarterKitsAsync(null); - } - - /// - public void StarterKitsAsync(object userState) - { - if ((this.StarterKitsOperationCompleted == null)) - { - this.StarterKitsOperationCompleted = new System.Threading.SendOrPostCallback(this.OnStarterKitsOperationCompleted); - } - this.InvokeAsync("StarterKits", new object[0], this.StarterKitsOperationCompleted, userState); - } - - private void OnStarterKitsOperationCompleted(object arg) - { - if ((this.StarterKitsCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.StarterKitsCompleted(this, new StarterKitsCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/StarterKitModulesCategorized", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Category[] StarterKitModulesCategorized() - { - object[] results = this.Invoke("StarterKitModulesCategorized", new object[0]); - return ((Category[])(results[0])); - } - - /// - public System.IAsyncResult BeginStarterKitModulesCategorized(System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("StarterKitModulesCategorized", new object[0], callback, asyncState); - } - - /// - public Category[] EndStarterKitModulesCategorized(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Category[])(results[0])); - } - - /// - public void StarterKitModulesCategorizedAsync() - { - this.StarterKitModulesCategorizedAsync(null); - } - - /// - public void StarterKitModulesCategorizedAsync(object userState) - { - if ((this.StarterKitModulesCategorizedOperationCompleted == null)) - { - this.StarterKitModulesCategorizedOperationCompleted = new System.Threading.SendOrPostCallback(this.OnStarterKitModulesCategorizedOperationCompleted); - } - this.InvokeAsync("StarterKitModulesCategorized", new object[0], this.StarterKitModulesCategorizedOperationCompleted, userState); - } - - private void OnStarterKitModulesCategorizedOperationCompleted(object arg) - { - if ((this.StarterKitModulesCategorizedCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.StarterKitModulesCategorizedCompleted(this, new StarterKitModulesCategorizedCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/StarterKitModules", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Package[] StarterKitModules() - { - object[] results = this.Invoke("StarterKitModules", new object[0]); - return ((Package[])(results[0])); - } - - /// - public System.IAsyncResult BeginStarterKitModules(System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("StarterKitModules", new object[0], callback, asyncState); - } - - /// - public Package[] EndStarterKitModules(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Package[])(results[0])); - } - - /// - public void StarterKitModulesAsync() - { - this.StarterKitModulesAsync(null); - } - - /// - public void StarterKitModulesAsync(object userState) - { - if ((this.StarterKitModulesOperationCompleted == null)) - { - this.StarterKitModulesOperationCompleted = new System.Threading.SendOrPostCallback(this.OnStarterKitModulesOperationCompleted); - } - this.InvokeAsync("StarterKitModules", new object[0], this.StarterKitModulesOperationCompleted, userState); - } - - private void OnStarterKitModulesOperationCompleted(object arg) - { - if ((this.StarterKitModulesCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.StarterKitModulesCompleted(this, new StarterKitModulesCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/authenticate", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public string authenticate(string email, string md5Password) - { - object[] results = this.Invoke("authenticate", new object[] { - email, - md5Password}); - return ((string)(results[0])); - } - - /// - public System.IAsyncResult Beginauthenticate(string email, string md5Password, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("authenticate", new object[] { - email, - md5Password}, callback, asyncState); - } - - /// - public string Endauthenticate(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((string)(results[0])); - } - - /// - public void authenticateAsync(string email, string md5Password) - { - this.authenticateAsync(email, md5Password, null); - } - - /// - public void authenticateAsync(string email, string md5Password, object userState) - { - if ((this.authenticateOperationCompleted == null)) - { - this.authenticateOperationCompleted = new System.Threading.SendOrPostCallback(this.OnauthenticateOperationCompleted); - } - this.InvokeAsync("authenticate", new object[] { - email, - md5Password}, this.authenticateOperationCompleted, userState); - } - - private void OnauthenticateOperationCompleted(object arg) - { - if ((this.authenticateCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.authenticateCompleted(this, new authenticateCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - - - - - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/GetPackageFile", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - [return: System.Xml.Serialization.XmlElementAttribute(DataType = "base64Binary")] - public byte[] GetPackageFile(string packageGuid, string umbracoVersion) - { - object[] results = this.Invoke("GetPackageFile", new object[] { - packageGuid, - umbracoVersion}); - return ((byte[])(results[0])); - } - - /// - public System.IAsyncResult BeginfetchPackageByVersion(string packageGuid, string umbracoVersion, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("GetPackageFile", new object[] { - packageGuid, - umbracoVersion}, callback, asyncState); - } - - /// - public byte[] EndGetPackageFile(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((byte[])(results[0])); - } - - /// - public void GetPackageFileAsync(string packageGuid, string umbracoVersion) - { - this.GetPackageFileAsync(packageGuid, umbracoVersion, null); - } - - /// - public void GetPackageFileAsync(string packageGuid, string umbracoVersion, object userState) - { - if ((this.GetPackageFileOperationCompleted == null)) - { - this.GetPackageFileOperationCompleted = new System.Threading.SendOrPostCallback(this.OnGetPackageFileOperationCompleted); - } - this.InvokeAsync("GetPackageFile", new object[] { - packageGuid, - umbracoVersion}, this.GetPackageFileOperationCompleted, userState); - } - - private void OnGetPackageFileOperationCompleted(object arg) - { - if ((this.GetPackageFileCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.GetPackageFileCompleted(this, new GetPackageFileCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - - - - - - - - - - - - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/fetchPackageByVersion", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - [return: System.Xml.Serialization.XmlElementAttribute(DataType = "base64Binary")] - public byte[] fetchPackageByVersion(string packageGuid, Version schemaVersion) - { - object[] results = this.Invoke("fetchPackageByVersion", new object[] { - packageGuid, - schemaVersion}); - return ((byte[])(results[0])); - } - - /// - public System.IAsyncResult BeginfetchPackageByVersion(string packageGuid, Version schemaVersion, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("fetchPackageByVersion", new object[] { - packageGuid, - schemaVersion}, callback, asyncState); - } - - /// - public byte[] EndfetchPackageByVersion(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((byte[])(results[0])); - } - - /// - public void fetchPackageByVersionAsync(string packageGuid, Version schemaVersion) - { - this.fetchPackageByVersionAsync(packageGuid, schemaVersion, null); - } - - /// - public void fetchPackageByVersionAsync(string packageGuid, Version schemaVersion, object userState) - { - if ((this.fetchPackageByVersionOperationCompleted == null)) - { - this.fetchPackageByVersionOperationCompleted = new System.Threading.SendOrPostCallback(this.OnfetchPackageByVersionOperationCompleted); - } - this.InvokeAsync("fetchPackageByVersion", new object[] { - packageGuid, - schemaVersion}, this.fetchPackageByVersionOperationCompleted, userState); - } - - private void OnfetchPackageByVersionOperationCompleted(object arg) - { - if ((this.fetchPackageByVersionCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.fetchPackageByVersionCompleted(this, new fetchPackageByVersionCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/fetchPackage", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - [return: System.Xml.Serialization.XmlElementAttribute(DataType = "base64Binary")] - public byte[] fetchPackage(string packageGuid) - { - object[] results = this.Invoke("fetchPackage", new object[] { - packageGuid}); - return ((byte[])(results[0])); - } - - /// - public System.IAsyncResult BeginfetchPackage(string packageGuid, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("fetchPackage", new object[] { - packageGuid}, callback, asyncState); - } - - /// - public byte[] EndfetchPackage(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((byte[])(results[0])); - } - - /// - public void fetchPackageAsync(string packageGuid) - { - this.fetchPackageAsync(packageGuid, null); - } - - /// - public void fetchPackageAsync(string packageGuid, object userState) - { - if ((this.fetchPackageOperationCompleted == null)) - { - this.fetchPackageOperationCompleted = new System.Threading.SendOrPostCallback(this.OnfetchPackageOperationCompleted); - } - this.InvokeAsync("fetchPackage", new object[] { - packageGuid}, this.fetchPackageOperationCompleted, userState); - } - - private void OnfetchPackageOperationCompleted(object arg) - { - if ((this.fetchPackageCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.fetchPackageCompleted(this, new fetchPackageCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/fetchProtectedPackage", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - [return: System.Xml.Serialization.XmlElementAttribute(DataType = "base64Binary")] - public byte[] fetchProtectedPackage(string packageGuid, string memberKey) - { - object[] results = this.Invoke("fetchProtectedPackage", new object[] { - packageGuid, - memberKey}); - return ((byte[])(results[0])); - } - - /// - public System.IAsyncResult BeginfetchProtectedPackage(string packageGuid, string memberKey, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("fetchProtectedPackage", new object[] { - packageGuid, - memberKey}, callback, asyncState); - } - - /// - public byte[] EndfetchProtectedPackage(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((byte[])(results[0])); - } - - /// - public void fetchProtectedPackageAsync(string packageGuid, string memberKey) - { - this.fetchProtectedPackageAsync(packageGuid, memberKey, null); - } - - /// - public void fetchProtectedPackageAsync(string packageGuid, string memberKey, object userState) - { - if ((this.fetchProtectedPackageOperationCompleted == null)) - { - this.fetchProtectedPackageOperationCompleted = new System.Threading.SendOrPostCallback(this.OnfetchProtectedPackageOperationCompleted); - } - this.InvokeAsync("fetchProtectedPackage", new object[] { - packageGuid, - memberKey}, this.fetchProtectedPackageOperationCompleted, userState); - } - - private void OnfetchProtectedPackageOperationCompleted(object arg) - { - if ((this.fetchProtectedPackageCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.fetchProtectedPackageCompleted(this, new fetchProtectedPackageCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/SubmitPackage", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public SubmitStatus SubmitPackage(string repositoryGuid, string authorGuid, string packageGuid, [System.Xml.Serialization.XmlElementAttribute(DataType = "base64Binary")] byte[] packageFile, [System.Xml.Serialization.XmlElementAttribute(DataType = "base64Binary")] byte[] packageDoc, [System.Xml.Serialization.XmlElementAttribute(DataType = "base64Binary")] byte[] packageThumbnail, string name, string author, string authorUrl, string description) - { - object[] results = this.Invoke("SubmitPackage", new object[] { - repositoryGuid, - authorGuid, - packageGuid, - packageFile, - packageDoc, - packageThumbnail, - name, - author, - authorUrl, - description}); - return ((SubmitStatus)(results[0])); - } - - /// - public System.IAsyncResult BeginSubmitPackage(string repositoryGuid, string authorGuid, string packageGuid, byte[] packageFile, byte[] packageDoc, byte[] packageThumbnail, string name, string author, string authorUrl, string description, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("SubmitPackage", new object[] { - repositoryGuid, - authorGuid, - packageGuid, - packageFile, - packageDoc, - packageThumbnail, - name, - author, - authorUrl, - description}, callback, asyncState); - } - - /// - public SubmitStatus EndSubmitPackage(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((SubmitStatus)(results[0])); - } - - /// - public void SubmitPackageAsync(string repositoryGuid, string authorGuid, string packageGuid, byte[] packageFile, byte[] packageDoc, byte[] packageThumbnail, string name, string author, string authorUrl, string description) - { - this.SubmitPackageAsync(repositoryGuid, authorGuid, packageGuid, packageFile, packageDoc, packageThumbnail, name, author, authorUrl, description, null); - } - - /// - public void SubmitPackageAsync(string repositoryGuid, string authorGuid, string packageGuid, byte[] packageFile, byte[] packageDoc, byte[] packageThumbnail, string name, string author, string authorUrl, string description, object userState) - { - if ((this.SubmitPackageOperationCompleted == null)) - { - this.SubmitPackageOperationCompleted = new System.Threading.SendOrPostCallback(this.OnSubmitPackageOperationCompleted); - } - this.InvokeAsync("SubmitPackage", new object[] { - repositoryGuid, - authorGuid, - packageGuid, - packageFile, - packageDoc, - packageThumbnail, - name, - author, - authorUrl, - description}, this.SubmitPackageOperationCompleted, userState); - } - - private void OnSubmitPackageOperationCompleted(object arg) - { - if ((this.SubmitPackageCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.SubmitPackageCompleted(this, new SubmitPackageCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/PackageByGuid", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Package PackageByGuid(string packageGuid) - { - object[] results = this.Invoke("PackageByGuid", new object[] { - packageGuid}); - return ((Package)(results[0])); - } - - /// - public System.IAsyncResult BeginPackageByGuid(string packageGuid, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("PackageByGuid", new object[] { - packageGuid}, callback, asyncState); - } - - /// - public Package EndPackageByGuid(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Package)(results[0])); - } - - /// - public void PackageByGuidAsync(string packageGuid) - { - this.PackageByGuidAsync(packageGuid, null); - } - - /// - public void PackageByGuidAsync(string packageGuid, object userState) - { - if ((this.PackageByGuidOperationCompleted == null)) - { - this.PackageByGuidOperationCompleted = new System.Threading.SendOrPostCallback(this.OnPackageByGuidOperationCompleted); - } - this.InvokeAsync("PackageByGuid", new object[] { - packageGuid}, this.PackageByGuidOperationCompleted, userState); - } - - private void OnPackageByGuidOperationCompleted(object arg) - { - if ((this.PackageByGuidCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.PackageByGuidCompleted(this, new PackageByGuidCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/SkinByGuid", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Skin SkinByGuid(string skinGuid) - { - object[] results = this.Invoke("SkinByGuid", new object[] { - skinGuid}); - return ((Skin)(results[0])); - } - - /// - public System.IAsyncResult BeginSkinByGuid(string skinGuid, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("SkinByGuid", new object[] { - skinGuid}, callback, asyncState); - } - - /// - public Skin EndSkinByGuid(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Skin)(results[0])); - } - - /// - public void SkinByGuidAsync(string skinGuid) - { - this.SkinByGuidAsync(skinGuid, null); - } - - /// - public void SkinByGuidAsync(string skinGuid, object userState) - { - if ((this.SkinByGuidOperationCompleted == null)) - { - this.SkinByGuidOperationCompleted = new System.Threading.SendOrPostCallback(this.OnSkinByGuidOperationCompleted); - } - this.InvokeAsync("SkinByGuid", new object[] { - skinGuid}, this.SkinByGuidOperationCompleted, userState); - } - - private void OnSkinByGuidOperationCompleted(object arg) - { - if ((this.SkinByGuidCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.SkinByGuidCompleted(this, new SkinByGuidCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://packages.umbraco.org/webservices/Skins", RequestNamespace = "http://packages.umbraco.org/webservices/", ResponseNamespace = "http://packages.umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public Skin[] Skins(string starterKitGuid) - { - object[] results = this.Invoke("Skins", new object[] { - starterKitGuid}); - return ((Skin[])(results[0])); - } - - /// - public System.IAsyncResult BeginSkins(string starterKitGuid, System.AsyncCallback callback, object asyncState) - { - return this.BeginInvoke("Skins", new object[] { - starterKitGuid}, callback, asyncState); - } - - /// - public Skin[] EndSkins(System.IAsyncResult asyncResult) - { - object[] results = this.EndInvoke(asyncResult); - return ((Skin[])(results[0])); - } - - /// - public void SkinsAsync(string starterKitGuid) - { - this.SkinsAsync(starterKitGuid, null); - } - - /// - public void SkinsAsync(string starterKitGuid, object userState) - { - if ((this.SkinsOperationCompleted == null)) - { - this.SkinsOperationCompleted = new System.Threading.SendOrPostCallback(this.OnSkinsOperationCompleted); - } - this.InvokeAsync("Skins", new object[] { - starterKitGuid}, this.SkinsOperationCompleted, userState); - } - - private void OnSkinsOperationCompleted(object arg) - { - if ((this.SkinsCompleted != null)) - { - System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); - this.SkinsCompleted(this, new SkinsCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); - } - } - - /// - public new void CancelAsync(object userState) - { - base.CancelAsync(userState); - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://packages.umbraco.org/webservices/")] - public partial class Category - { - - private string textField; - - private string descriptionField; - - private string urlField; - - private int idField; - - private Package[] packagesField; - - /// - public string Text - { - get - { - return this.textField; - } - set - { - this.textField = value; - } - } - - /// - public string Description - { - get - { - return this.descriptionField; - } - set - { - this.descriptionField = value; - } - } - - /// - public string Url - { - get - { - return this.urlField; - } - set - { - this.urlField = value; - } - } - - /// - public int Id - { - get - { - return this.idField; - } - set - { - this.idField = value; - } - } - - /// - public Package[] Packages - { - get - { - return this.packagesField; - } - set - { - this.packagesField = value; - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://packages.umbraco.org/webservices/")] - public partial class Package - { - - private System.Guid repoGuidField; - - private string textField; - - private string descriptionField; - - private string iconField; - - private string thumbnailField; - - private string documentationField; - - private string demoField; - - private bool acceptedField; - - private bool editorsPickField; - - private bool protectedField; - - private bool hasUpgradeField; - - private string upgradeVersionField; - - private string upgradeReadMeField; - - private string urlField; - - /// - public System.Guid RepoGuid - { - get - { - return this.repoGuidField; - } - set - { - this.repoGuidField = value; - } - } - - /// - public string Text - { - get - { - return this.textField; - } - set - { - this.textField = value; - } - } - - /// - public string Description - { - get - { - return this.descriptionField; - } - set - { - this.descriptionField = value; - } - } - - /// - public string Icon - { - get - { - return this.iconField; - } - set - { - this.iconField = value; - } - } - - /// - public string Thumbnail - { - get - { - return this.thumbnailField; - } - set - { - this.thumbnailField = value; - } - } - - /// - public string Documentation - { - get - { - return this.documentationField; - } - set - { - this.documentationField = value; - } - } - - /// - public string Demo - { - get - { - return this.demoField; - } - set - { - this.demoField = value; - } - } - - /// - public bool Accepted - { - get - { - return this.acceptedField; - } - set - { - this.acceptedField = value; - } - } - - /// - public bool EditorsPick - { - get - { - return this.editorsPickField; - } - set - { - this.editorsPickField = value; - } - } - - /// - public bool Protected - { - get - { - return this.protectedField; - } - set - { - this.protectedField = value; - } - } - - /// - public bool HasUpgrade - { - get - { - return this.hasUpgradeField; - } - set - { - this.hasUpgradeField = value; - } - } - - /// - public string UpgradeVersion - { - get - { - return this.upgradeVersionField; - } - set - { - this.upgradeVersionField = value; - } - } - - /// - public string UpgradeReadMe - { - get - { - return this.upgradeReadMeField; - } - set - { - this.upgradeReadMeField = value; - } - } - - /// - public string Url - { - get - { - return this.urlField; - } - set - { - this.urlField = value; - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://packages.umbraco.org/webservices/")] - public partial class Skin - { - - private System.Guid repoGuidField; - - private string textField; - - private string thumbnailField; - - private string previewField; - - private string descriptionField; - - private string authorField; - - private string authorUrlField; - - private string licenseField; - - private string licenseUrlField; - - /// - public System.Guid RepoGuid - { - get - { - return this.repoGuidField; - } - set - { - this.repoGuidField = value; - } - } - - /// - public string Text - { - get - { - return this.textField; - } - set - { - this.textField = value; - } - } - - /// - public string Thumbnail - { - get - { - return this.thumbnailField; - } - set - { - this.thumbnailField = value; - } - } - - /// - public string Preview - { - get - { - return this.previewField; - } - set - { - this.previewField = value; - } - } - - /// - public string Description - { - get - { - return this.descriptionField; - } - set - { - this.descriptionField = value; - } - } - - /// - public string Author - { - get - { - return this.authorField; - } - set - { - this.authorField = value; - } - } - - /// - public string AuthorUrl - { - get - { - return this.authorUrlField; - } - set - { - this.authorUrlField = value; - } - } - - /// - public string License - { - get - { - return this.licenseField; - } - set - { - this.licenseField = value; - } - } - - /// - public string LicenseUrl - { - get - { - return this.licenseUrlField; - } - set - { - this.licenseUrlField = value; - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://packages.umbraco.org/webservices/")] - public enum Version - { - - /// - Version3, - - /// - Version4, - - /// - /// This is apparently the version number we pass in for all installs - /// - Version41, - - /// - Version41Legacy, - - /// - Version50, - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://packages.umbraco.org/webservices/")] - public enum SubmitStatus - { - - /// - Complete, - - /// - Exists, - - /// - NoAccess, - - /// - Error, - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void CategoriesCompletedEventHandler(object sender, CategoriesCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class CategoriesCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal CategoriesCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Category[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Category[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void NitrosCompletedEventHandler(object sender, NitrosCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class NitrosCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal NitrosCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Package[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Package[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void NitrosByVersionCompletedEventHandler(object sender, NitrosByVersionCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class NitrosByVersionCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal NitrosByVersionCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Package[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Package[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void NitrosCategorizedCompletedEventHandler(object sender, NitrosCategorizedCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class NitrosCategorizedCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal NitrosCategorizedCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Category[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Category[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void NitrosCategorizedByVersionCompletedEventHandler(object sender, NitrosCategorizedByVersionCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class NitrosCategorizedByVersionCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal NitrosCategorizedByVersionCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Category[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Category[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void StarterKitsCompletedEventHandler(object sender, StarterKitsCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class StarterKitsCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal StarterKitsCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Package[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Package[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void StarterKitModulesCategorizedCompletedEventHandler(object sender, StarterKitModulesCategorizedCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class StarterKitModulesCategorizedCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal StarterKitModulesCategorizedCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Category[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Category[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void StarterKitModulesCompletedEventHandler(object sender, StarterKitModulesCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class StarterKitModulesCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal StarterKitModulesCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Package[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Package[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void authenticateCompletedEventHandler(object sender, authenticateCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class authenticateCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal authenticateCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public string Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((string)(this.results[0])); - } - } - } - - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void GetPackageFileCompletedEventHandler(object sender, GetPackageFileCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class GetPackageFileCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal GetPackageFileCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public byte[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((byte[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void fetchPackageByVersionCompletedEventHandler(object sender, fetchPackageByVersionCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class fetchPackageByVersionCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal fetchPackageByVersionCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public byte[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((byte[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void fetchPackageCompletedEventHandler(object sender, fetchPackageCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class fetchPackageCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal fetchPackageCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public byte[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((byte[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void fetchProtectedPackageCompletedEventHandler(object sender, fetchProtectedPackageCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class fetchProtectedPackageCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal fetchProtectedPackageCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public byte[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((byte[])(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void SubmitPackageCompletedEventHandler(object sender, SubmitPackageCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class SubmitPackageCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal SubmitPackageCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public SubmitStatus Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((SubmitStatus)(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void PackageByGuidCompletedEventHandler(object sender, PackageByGuidCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class PackageByGuidCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal PackageByGuidCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Package Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Package)(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void SkinByGuidCompletedEventHandler(object sender, SkinByGuidCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class SkinByGuidCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal SkinByGuidCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Skin Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Skin)(this.results[0])); - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - public delegate void SkinsCompletedEventHandler(object sender, SkinsCompletedEventArgs e); - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class SkinsCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs - { - - private object[] results; - - internal SkinsCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : - base(exception, cancelled, userState) - { - this.results = results; - } - - /// - public Skin[] Result - { - get - { - this.RaiseExceptionIfNecessary(); - return ((Skin[])(this.results[0])); - } - } - } -} diff --git a/src/umbraco.cms/umbraco.cms.csproj b/src/umbraco.cms/umbraco.cms.csproj index 42db5b90ee..c38dbb2d2c 100644 --- a/src/umbraco.cms/umbraco.cms.csproj +++ b/src/umbraco.cms/umbraco.cms.csproj @@ -346,8 +346,6 @@ Code - - Code From 6c8e39876fa9cc6d577a711ae31ba930a47b7629 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 19 Apr 2018 19:36:32 +0200 Subject: [PATCH 04/97] These changes shouldn't have been checked in --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index fa3ff3ca72..3a7cbc4429 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -49,7 +49,6 @@ true true -
    bin\ @@ -1037,7 +1036,7 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" True 7110 / - http://localhost:3110 + http://localhost:7110 False False From 8fc33f6aea7a5a96089af668548c71a5e24a84d6 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 19 Apr 2018 19:36:44 +0200 Subject: [PATCH 05/97] Fix unit tests --- .../UmbracoSettings/umbracoSettings.minimal.config | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.minimal.config b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.minimal.config index f74879b193..b6ea54d75e 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.minimal.config +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.minimal.config @@ -60,9 +60,6 @@ - - - @@ -72,4 +69,4 @@ - \ No newline at end of file + From 386dc1a14d39dba5e41cdb714be3c47e0ffbf76f Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 19 Apr 2018 19:54:53 +0200 Subject: [PATCH 06/97] U4-11253 New Chrome 66 trimStart function is overriding the Umbraco Core trimStart polyfill --- .../lib/umbraco/Extensions.js | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index b70a6b12bc..e8c6eda4bc 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -84,27 +84,21 @@ }; } - if (!String.prototype.trimStart) { - - /** trims the start of the string*/ - String.prototype.trimStart = function (str) { - if (this.startsWith(str)) { - return this.substring(str.length); - } - return this; - }; - } - - if (!String.prototype.trimEnd) { + /** trims the start of the string*/ + String.prototype.trimStart = function (str) { + if (this.startsWith(str)) { + return this.substring(str.length); + } + return this; + }; - /** trims the end of the string*/ - String.prototype.trimEnd = function (str) { - if (this.endsWith(str)) { - return this.substring(0, this.length - str.length); - } - return this; - }; - } + /** trims the end of the string*/ + String.prototype.trimEnd = function (str) { + if (this.endsWith(str)) { + return this.substring(0, this.length - str.length); + } + return this; + }; if (!String.prototype.utf8Encode) { @@ -332,4 +326,4 @@ } -})(); \ No newline at end of file +})(); From fbc9c6aed630758222a1750ca47045af49ef8123 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 19 Apr 2018 20:06:34 +0200 Subject: [PATCH 07/97] Fixes even more (hopefully all) unit tests --- .../UmbracoSettings/umbracoSettings.config | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config index a89a6e4d71..5f5d47e8ba 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config @@ -222,13 +222,6 @@ - - - - - - - @@ -256,4 +249,4 @@ internalRedirectPreservesTemplate="false"> - \ No newline at end of file + From 6d6be4e6a42a34293c67aea3accdf658c99f0a44 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 19 Apr 2018 20:29:46 +0200 Subject: [PATCH 08/97] Fixes last failing test --- src/Umbraco.Tests/Plugins/PluginManagerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs index 22d150ac3b..970259618a 100644 --- a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs +++ b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs @@ -300,7 +300,7 @@ AnotherContentFinder public void Resolves_Trees() { var trees = _manager.ResolveTrees(); - Assert.AreEqual(35, trees.Count()); + Assert.AreEqual(34, trees.Count()); } [Test] From e9f0e98d2c2847f5f5e7195de7d51b5107837bc8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Apr 2018 13:12:55 +1000 Subject: [PATCH 09/97] Updates EntityRepository to get the variant names for content items ... for the non-paged query which we can use in the tree --- .../Implement/EntityRepository.cs | 109 ++++++++++++++++-- .../Repositories/EntityRepositoryTests.cs | 12 -- .../Services/EntityServiceTests.cs | 56 ++++++++- src/Umbraco.Tests/Umbraco.Tests.csproj | 6 +- .../Trees/ContentTreeControllerBase.cs | 2 +- 5 files changed, 154 insertions(+), 31 deletions(-) delete mode 100644 src/Umbraco.Tests/Persistence/Repositories/EntityRepositoryTests.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs index 857e059942..6e34030b29 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs @@ -128,6 +128,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var dtos = page.Items; var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); + //TODO: For isContent will we need to build up the variation info? + if (isMedia) BuildProperties(entities, dtos); @@ -251,18 +253,35 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var isMedia = objectType == Constants.ObjectTypes.Media; var sql = GetBaseWhere(isContent, isMedia, false, null, objectType); + var translator = new SqlTranslator(sql, query); sql = translator.Translate(); sql = AddGroupBy(isContent, isMedia, sql); - var dtos = Database.Fetch(sql); - if (dtos.Count == 0) return Enumerable.Empty(); - var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); + //isContent is going to return a 1:M result now with the variants so we need to do different things + if (isContent) + { + var dtos = Database.FetchOneToMany( + dto => dto.VariationInfo, + dto => dto.VersionId, + sql); + if (dtos.Count == 0) return Enumerable.Empty(); + var entities = dtos.Select(x => BuildDocumentEntity(x)).ToArray(); + return entities; + } + else + { + var dtos = Database.Fetch(sql); - if (isMedia) - BuildProperties(entities, dtos); + if (dtos.Count == 0) return Enumerable.Empty(); - return entities; + var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); + + if (isMedia) + BuildProperties(entities, dtos); + + return entities; + } } public UmbracoObjectTypes GetObjectType(int id) @@ -409,8 +428,33 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // return wrappedSql; //} - // the DTO corresponding to fields selected by GetBase + + /// + /// The DTO used to fetch results for a content item with it's variation info + /// + private class ContentEntityDto : BaseDto + { + [ResultColumn, Reference(ReferenceType.Many)] + public List VariationInfo { get; set; } + } + + /// + /// The DTO used in the 1:M result for content variation info + /// + private class ContentEntityVariationInfoDto + { + [Column("versionCultureId")] + public int VersionCultureId { get; set; } + [Column("versionCultureLangId")] + public int LanguageId { get; set; } + [Column("versionCultureName")] + public string Name { get; set; } + } + // ReSharper disable once ClassNeverInstantiated.Local + /// + /// the DTO corresponding to fields selected by GetBase + /// private class BaseDto { // ReSharper disable UnusedAutoPropertyAccessor.Local @@ -455,14 +499,21 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .AndSelect(x => x.SortOrder, x => x.UniqueId, x => x.Text, x => x.NodeObjectType, x => x.CreateDate) .Append(", COUNT(child.id) AS children"); - if (isContent) - sql - .AndSelect(x => x.Published, x => x.Edited); - if (isContent || isMedia) sql .AndSelect(x => NPocoSqlExtensions.Statics.Alias(x.Id, "versionId")) .AndSelect(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer); + + if (isContent) + { + sql + .AndSelect(x => x.Published, x => x.Edited) + //This MUST come last in the select statements since we will end up with a 1:M query + .AndSelect( + x => NPocoSqlExtensions.Statics.Alias(x.Id, "versionCultureId"), + x => NPocoSqlExtensions.Statics.Alias(x.LanguageId, "versionCultureLangId"), + x => NPocoSqlExtensions.Statics.Alias(x.Name, "versionCultureName")); + } } sql @@ -482,10 +533,18 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .InnerJoin().On((left, right) => left.NodeId == right.NodeId); } + //Any LeftJoin statements need to come last if (isCount == false) + { sql .LeftJoin("child").On((left, right) => left.NodeId == right.ParentId, aliasRight: "child"); + if (isContent) + sql + .LeftJoin().On((left, right) => left.Id == right.VersionId); + } + + filter?.Invoke(sql); return sql; @@ -542,8 +601,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .AndBy(x => x.SortOrder, x => x.UniqueId, x => x.Text, x => x.NodeObjectType, x => x.CreateDate); if (isContent) + { sql - .AndBy(x => x.Published, x => x.Edited); + .AndBy(x => x.Published, x => x.Edited) + .AndBy(x => x.Id, x => x.LanguageId, x => x.Name); + } + if (isContent || isMedia) sql @@ -819,6 +882,28 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return entity; } + /// + /// Builds the from a and ensures the AdditionalData is populated with variant info + /// + /// + /// + private static EntitySlim BuildDocumentEntity(ContentEntityDto dto) + { + // EntitySlim does not track changes + var entity = new DocumentEntitySlim(); + BuildDocumentEntity(entity, dto); + var variantInfo = new Dictionary(); + if (dto.VariationInfo != null) + { + foreach (var info in dto.VariationInfo) + { + variantInfo[info.LanguageId] = info.Name; + } + entity.AdditionalData["VariantInfo"] = variantInfo; + } + return entity; + } + #endregion } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/EntityRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/EntityRepositoryTests.cs deleted file mode 100644 index 2357ba273a..0000000000 --- a/src/Umbraco.Tests/Persistence/Repositories/EntityRepositoryTests.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Umbraco.Tests.Persistence.Repositories -{ - class EntityRepositoryTests - { - } -} diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index b8d24d940c..d7acc6e177 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -427,6 +427,60 @@ namespace Umbraco.Tests.Services Assert.That(entities.Any(), Is.True); Assert.That(entities.Length, Is.EqualTo(1)); Assert.That(entities.Any(x => x.Trashed), Is.False); + } + + [Test] + public void EntityService_Can_Get_Child_Content_By_ParentId_And_UmbracoObjectType_With_Variant_Names() + { + var service = ServiceContext.EntityService; + + var langFr = new Language("fr-FR"); + var langEs = new Language("es-ES"); + ServiceContext.LocalizationService.Save(langFr); + ServiceContext.LocalizationService.Save(langEs); + + var contentType = MockedContentTypes.CreateSimpleContentType("test1", "Test1", false); + contentType.Variations = ContentVariation.CultureNeutral; + ServiceContext.ContentTypeService.Save(contentType); + + var root = MockedContent.CreateSimpleContent(contentType); + ServiceContext.ContentService.Save(root); + + for (int i = 0; i < 10; i++) + { + var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root); + if (i % 2 == 0) + { + c1.SetName(langFr.Id, "Test " + i + " - FR"); + c1.SetName(langEs.Id, "Test " + i + " - ES"); + } + ServiceContext.ContentService.Save(c1); + } + + var entities = service.GetChildren(root.Id, UmbracoObjectTypes.Document).ToArray(); + + Assert.AreEqual(10, entities.Length); + + for (int i = 0; i < entities.Length; i++) + { + if (i % 2 == 0) + { + Assert.AreEqual(1, entities[i].AdditionalData.Count); + Assert.AreEqual("VariantInfo", entities[i].AdditionalData.Keys.First()); + var variantInfo = entities[i].AdditionalData.First().Value as IDictionary; + Assert.IsNotNull(variantInfo); + var keys = variantInfo.Keys.ToList(); + var vals = variantInfo.Values.ToList(); + Assert.AreEqual(langFr.Id, keys[0]); + Assert.AreEqual("Test " + i + " - FR", vals[0]); + Assert.AreEqual(langEs.Id, keys[1]); + Assert.AreEqual("Test " + i + " - ES", vals[1]); + } + else + { + Assert.AreEqual(0, entities[i].AdditionalData.Count); + } + } } [Test] @@ -518,7 +572,7 @@ namespace Umbraco.Tests.Services } Assert.That(entities.Any(x => - x.AdditionalData.Any(y => y.Value is EntitySlim.PropertySlim && ((EntitySlim.PropertySlim) y.Value).PropertyEditorAlias == Constants.PropertyEditors.Aliases.UploadField)), Is.True); + x.AdditionalData.Any(y => y.Value is EntitySlim.PropertySlim && ((EntitySlim.PropertySlim)y.Value).PropertyEditorAlias == Constants.PropertyEditors.Aliases.UploadField)), Is.True); } [Test] diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f630194a41..3da04b48ca 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -143,7 +143,6 @@ - @@ -606,11 +605,9 @@ $(NuGetPackageFolders.Split(';')[0]) - - - + @@ -624,5 +621,4 @@ - \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index b6ab625b78..488b1097c0 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -217,7 +217,7 @@ namespace Umbraco.Web.Trees // we'll mock using this and it will just be some mock data foreach(var e in result) { - if (e.AdditionalData.TryGetValue("VariantNames", out var variantNames)) + if (e.AdditionalData.TryGetValue("VariantInfo", out var variantNames)) { var casted = (IDictionary)variantNames; e.Name = casted[langId.Value]; From d99beb0c8b59db9743c3f9ee1bcdcd46161d1e14 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Apr 2018 13:26:45 +1000 Subject: [PATCH 10/97] Gets the tree to display the culture specific name --- .../Implement/EntityRepository.cs | 2 +- .../Services/EntityServiceTests.cs | 2 +- .../Trees/ContentTreeControllerBase.cs | 30 ++++++++++--------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs index 6e34030b29..e1499b8f61 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs @@ -899,7 +899,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { variantInfo[info.LanguageId] = info.Name; } - entity.AdditionalData["VariantInfo"] = variantInfo; + entity.AdditionalData["CultureNames"] = variantInfo; } return entity; } diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index d7acc6e177..ebf4ea4cb3 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -466,7 +466,7 @@ namespace Umbraco.Tests.Services if (i % 2 == 0) { Assert.AreEqual(1, entities[i].AdditionalData.Count); - Assert.AreEqual("VariantInfo", entities[i].AdditionalData.Keys.First()); + Assert.AreEqual("CultureNames", entities[i].AdditionalData.Keys.First()); var variantInfo = entities[i].AdditionalData.First().Value as IDictionary; Assert.IsNotNull(variantInfo); var keys = variantInfo.Keys.ToList(); diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 488b1097c0..abab847151 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -209,22 +209,24 @@ namespace Umbraco.Web.Trees { result = Services.EntityService.GetChildren(entityId, UmbracoObjectType).ToArray(); } - - if (langId.HasValue) + + //This should really never be null, but we'll error check anyways + int? currLangId = langId.HasValue ? langId.Value : Services.LocalizationService.GetDefaultVariantLanguage()?.Id; + + //Try to see if there is a variant name for the current language for the item and set the name accordingly. + //If any of this fails, the tree node name will remain the default invariant culture name. + //fixme - what if there is no name found at all ? This could occur if the doc type is variant and the user fills in all language values, then creates a new lang, sets it as default and deletes all other pre-existing langs + if (currLangId.HasValue) { - //need to update all node names - //TODO: This is not currently stored, we need to wait until U4-11128 is complete for this to work, in the meantime - // we'll mock using this and it will just be some mock data - foreach(var e in result) + foreach (var e in result) { - if (e.AdditionalData.TryGetValue("VariantInfo", out var variantNames)) - { - var casted = (IDictionary)variantNames; - e.Name = casted[langId.Value]; - } - else - { - e.Name = e.Name + " (lang: " + langId.Value + ")"; + if (e.AdditionalData.TryGetValue("CultureNames", out var cultureNames) + && cultureNames is IDictionary cnd) + { + if (cnd.TryGetValue(currLangId.Value, out var name)) + { + e.Name = name; + } } } } From 9325d30e7ca5e9c1e59dc629cc75d1e9cd165809 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Apr 2018 13:44:20 +1000 Subject: [PATCH 11/97] adds some notes to the setVariantStatusColor --- .../editor/umbeditorheader.directive.js | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js index 520962a084..6a15b0baba 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js @@ -225,18 +225,23 @@ Use this directive to construct a header inside the main editor window. }); } + //TODO: This doesn't really affect any UI currently, need some feedback from mads function setVariantStatusColor(variants) { angular.forEach(variants, function (variant) { - angular.forEach(variant.states, function(state){ - switch (state.name) { - case "Published": - case "Published +": - state.stateColor = "success"; - break; - default: - state.stateColor = "gray"; - } - }); + + //TODO: What about variant.exists? If we are applying colors/styles, this should be one of them + + switch (variant.state) { + case "Published": + variant.stateColor = "success"; + break; + case "Unpublished": + //TODO: Not sure if these statuses will ever bubble up to the UI? + case "Publishing": + case "Unpublishing": + default: + variant.stateColor = "gray"; + } }); } From 5dfa0e744642a75157e1b8dad158dd7afd1f95e4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Apr 2018 16:07:34 +1000 Subject: [PATCH 12/97] Updates EntityRepository to support normal get operations adds another unit tests, adds notes. --- .../Implement/EntityRepository.cs | 80 ++++++++++++++----- .../Services/EntityServiceTests.cs | 61 +++++++++++--- .../Trees/ContentTreeControllerBase.cs | 5 +- 3 files changed, 114 insertions(+), 32 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs index e1499b8f61..cb0d19cc2e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs @@ -150,15 +150,29 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var isMedia = objectTypeId == Constants.ObjectTypes.Media; var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, key); - var dto = Database.FirstOrDefault(sql); - if (dto == null) return null; - var entity = BuildEntity(isContent, isMedia, dto); + //isContent is going to return a 1:M result now with the variants so we need to do different things + if (isContent) + { + var dtos = Database.FetchOneToMany( + dto => dto.VariationInfo, + dto => dto.VersionId, + sql); + if (dtos.Count == 0) return null; + return BuildDocumentEntity(dtos[0]); + } + else + { + var dto = Database.FirstOrDefault(sql); + if (dto == null) return null; - if (isMedia) - BuildProperties(entity, dto); + var entity = BuildEntity(isContent, isMedia, dto); - return entity; + if (isMedia) + BuildProperties(entity, dto); + + return entity; + } } public virtual IEntitySlim Get(int id) @@ -174,15 +188,30 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var isMedia = objectTypeId == Constants.ObjectTypes.Media; var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, id); - var dto = Database.FirstOrDefault(sql); - if (dto == null) return null; - var entity = BuildEntity(isContent, isMedia, dto); + //isContent is going to return a 1:M result now with the variants so we need to do different things + if (isContent) + { + var dtos = Database.FetchOneToMany( + dto => dto.VariationInfo, + dto => dto.VersionId, + sql); + if (dtos.Count == 0) return null; + return BuildDocumentEntity(dtos[0]); + } + else + { + var dto = Database.FirstOrDefault(sql); + if (dto == null) return null; - if (isMedia) - BuildProperties(entity, dto); + var entity = BuildEntity(isContent, isMedia, dto); + + if (isMedia) + BuildProperties(entity, dto); + + return entity; + } - return entity; } public virtual IEnumerable GetAll(Guid objectType, params int[] ids) @@ -205,15 +234,30 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var isMedia = objectType == Constants.ObjectTypes.Media; var sql = GetFullSqlForEntityType(isContent, isMedia, objectType, filter); - var dtos = Database.Fetch(sql); - if (dtos.Count == 0) return Enumerable.Empty(); - var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); + //isContent is going to return a 1:M result now with the variants so we need to do different things + if (isContent) + { + var dtos = Database.FetchOneToMany( + dto => dto.VariationInfo, + dto => dto.VersionId, + sql); + if (dtos.Count == 0) return Enumerable.Empty(); + var entities = dtos.Select(x => BuildDocumentEntity(x)).ToArray(); + return entities; + } + else + { + var dtos = Database.Fetch(sql); + if (dtos.Count == 0) return Enumerable.Empty(); - if (isMedia) - BuildProperties(entities, dtos); + var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); - return entities; + if (isMedia) + BuildProperties(entities, dtos); + + return entities; + } } public virtual IEnumerable GetAllPaths(Guid objectType, params int[] ids) diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index ebf4ea4cb3..124c77846d 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -19,7 +19,23 @@ namespace Umbraco.Tests.Services [Apartment(ApartmentState.STA)] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] public class EntityServiceTests : TestWithSomeContentBase - { + { + private Language _langFr; + private Language _langEs; + + public override void SetUp() + { + base.SetUp(); + + if (_langFr == null && _langEs == null) + { + _langFr = new Language("fr-FR"); + _langEs = new Language("es-ES"); + ServiceContext.LocalizationService.Save(_langFr); + ServiceContext.LocalizationService.Save(_langEs); + } + } + [Test] public void EntityService_Can_Get_Paged_Content_Children() { @@ -429,30 +445,49 @@ namespace Umbraco.Tests.Services Assert.That(entities.Any(x => x.Trashed), Is.False); } + [Test] + public void EntityService_Can_Get_Content_By_UmbracoObjectType_With_Variant_Names() + { + var service = ServiceContext.EntityService; + + + var alias = "test" + Guid.NewGuid(); + var contentType = MockedContentTypes.CreateSimpleContentType(alias, alias, false); + contentType.Variations = ContentVariation.CultureNeutral; + ServiceContext.ContentTypeService.Save(contentType); + + var c1 = MockedContent.CreateSimpleContent(contentType, "Test", -1); + c1.SetName(_langFr.Id, "Test - FR"); + c1.SetName(_langEs.Id, "Test - ES"); + ServiceContext.ContentService.Save(c1); + + var result = service.Get(c1.Id, UmbracoObjectTypes.Document); + Assert.AreEqual("Test", result.Name); + Assert.IsTrue(result.AdditionalData.ContainsKey("CultureNames")); + var cultureNames = (IDictionary)result.AdditionalData["CultureNames"]; + Assert.AreEqual("Test - FR", cultureNames[_langFr.Id]); + Assert.AreEqual("Test - ES", cultureNames[_langEs.Id]); + } + [Test] public void EntityService_Can_Get_Child_Content_By_ParentId_And_UmbracoObjectType_With_Variant_Names() { - var service = ServiceContext.EntityService; + var service = ServiceContext.EntityService; - var langFr = new Language("fr-FR"); - var langEs = new Language("es-ES"); - ServiceContext.LocalizationService.Save(langFr); - ServiceContext.LocalizationService.Save(langEs); - var contentType = MockedContentTypes.CreateSimpleContentType("test1", "Test1", false); contentType.Variations = ContentVariation.CultureNeutral; ServiceContext.ContentTypeService.Save(contentType); var root = MockedContent.CreateSimpleContent(contentType); - ServiceContext.ContentService.Save(root); - + ServiceContext.ContentService.Save(root); + for (int i = 0; i < 10; i++) { var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root); if (i % 2 == 0) { - c1.SetName(langFr.Id, "Test " + i + " - FR"); - c1.SetName(langEs.Id, "Test " + i + " - ES"); + c1.SetName(_langFr.Id, "Test " + i + " - FR"); + c1.SetName(_langEs.Id, "Test " + i + " - ES"); } ServiceContext.ContentService.Save(c1); } @@ -471,9 +506,9 @@ namespace Umbraco.Tests.Services Assert.IsNotNull(variantInfo); var keys = variantInfo.Keys.ToList(); var vals = variantInfo.Values.ToList(); - Assert.AreEqual(langFr.Id, keys[0]); + Assert.AreEqual(_langFr.Id, keys[0]); Assert.AreEqual("Test " + i + " - FR", vals[0]); - Assert.AreEqual(langEs.Id, keys[1]); + Assert.AreEqual(_langEs.Id, keys[1]); Assert.AreEqual("Test " + i + " - ES", vals[1]); } else diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index abab847151..96a1029f3c 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -215,7 +215,10 @@ namespace Umbraco.Web.Trees //Try to see if there is a variant name for the current language for the item and set the name accordingly. //If any of this fails, the tree node name will remain the default invariant culture name. - //fixme - what if there is no name found at all ? This could occur if the doc type is variant and the user fills in all language values, then creates a new lang, sets it as default and deletes all other pre-existing langs + + //fixme - what if there is no name found at all ? This could occur if the doc type is variant and the user fills in all language values, then creates a new lang and sets it as the default + //fixme - what if the user changes this document type to not allow culture variants after it's already been created with culture variants, this means we'll be displaying the culture variant name when in fact we should be displaying the invariant name... but that would be null + if (currLangId.HasValue) { foreach (var e in result) From 7d349ef51830664e65b70b00ddf4482da14788a4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Apr 2018 17:40:01 +1000 Subject: [PATCH 13/97] Replaces instances of UmbracoTemplatePage with UmbracoViewPage --- src/Umbraco.Core/IO/ViewHelper.cs | 7 +++---- src/Umbraco.Core/Services/Implement/FileService.cs | 4 ++-- .../Repositories/TemplateRepositoryTest.cs | 4 ++-- .../Services/Importing/PackageImportTests.cs | 2 +- src/Umbraco.Tests/Templates/ViewHelperTests.cs | 14 +++++++------- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 1 + .../umbraco_client/PunyCode/punycode.min.js | 2 ++ src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 2 +- 8 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 src/Umbraco.Web.UI/umbraco_client/PunyCode/punycode.min.js diff --git a/src/Umbraco.Core/IO/ViewHelper.cs b/src/Umbraco.Core/IO/ViewHelper.cs index 1645355b72..63f7504f18 100644 --- a/src/Umbraco.Core/IO/ViewHelper.cs +++ b/src/Umbraco.Core/IO/ViewHelper.cs @@ -67,10 +67,9 @@ namespace Umbraco.Core.IO modelNamespaceAlias = "ContentModels"; // either - // @inherits Umbraco.Web.Mvc.UmbracoTemplatePage - // @inherits Umbraco.Web.Mvc.UmbracoTemplatePage - // @inherits Umbraco.Web.Mvc.UmbracoTemplatePage - content.Append("@inherits Umbraco.Web.Mvc.UmbracoTemplatePage"); + // @inherits Umbraco.Web.Mvc.UmbracoViewPage + // @inherits Umbraco.Web.Mvc.UmbracoViewPage + content.Append("@inherits Umbraco.Web.Mvc.UmbracoViewPage"); if (modelClassName.IsNullOrWhiteSpace() == false) { content.Append("<"); diff --git a/src/Umbraco.Core/Services/Implement/FileService.cs b/src/Umbraco.Core/Services/Implement/FileService.cs index 23a4cddc55..8c1fa53880 100644 --- a/src/Umbraco.Core/Services/Implement/FileService.cs +++ b/src/Umbraco.Core/Services/Implement/FileService.cs @@ -27,7 +27,7 @@ namespace Umbraco.Core.Services.Implement private readonly IXsltFileRepository _xsltRepository; private readonly IAuditRepository _auditRepository; - private const string PartialViewHeader = "@inherits Umbraco.Web.Mvc.UmbracoTemplatePage"; + private const string PartialViewHeader = "@inherits Umbraco.Web.Mvc.UmbracoViewPage"; private const string PartialViewMacroHeader = "@inherits Umbraco.Web.Macros.PartialViewMacroPage"; public FileService(IScopeProvider uowProvider, ILogger logger, IEventMessagesFactory eventMessagesFactory, @@ -1182,4 +1182,4 @@ namespace Umbraco.Core.Services.Implement #endregion } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index e288bc5eaf..39128eb60a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -178,7 +178,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(repository.Get("test"), Is.Not.Null); Assert.That(_viewsFileSystem.FileExists("test.cshtml"), Is.True); Assert.AreEqual( - @"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage @{ Layout = null;}".StripWhitespace(), + @"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null;}".StripWhitespace(), template.Content.StripWhitespace()); } @@ -208,7 +208,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(repository.Get("test2"), Is.Not.Null); Assert.That(_viewsFileSystem.FileExists("test2.cshtml"), Is.True); Assert.AreEqual( - "@inherits Umbraco.Web.Mvc.UmbracoTemplatePage @{ Layout = \"test.cshtml\";}".StripWhitespace(), + "@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = \"test.cshtml\";}".StripWhitespace(), template2.Content.StripWhitespace()); } } diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs index 1333ca9f57..a810d4c644 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs @@ -189,7 +189,7 @@ namespace Umbraco.Tests.Services.Importing Assert.That(templates.Count(), Is.EqualTo(numberOfTemplates)); Assert.AreEqual(init + numberOfTemplates, allTemplates.Count()); - Assert.IsTrue(allTemplates.All(x => x.Content.Contains("UmbracoTemplatePage"))); + Assert.IsTrue(allTemplates.All(x => x.Content.Contains("UmbracoViewPage"))); } [Test] diff --git a/src/Umbraco.Tests/Templates/ViewHelperTests.cs b/src/Umbraco.Tests/Templates/ViewHelperTests.cs index 5bd7733920..ee7264eaec 100644 --- a/src/Umbraco.Tests/Templates/ViewHelperTests.cs +++ b/src/Umbraco.Tests/Templates/ViewHelperTests.cs @@ -10,7 +10,7 @@ namespace Umbraco.Tests.Templates public void NoOptions() { var view = ViewHelper.GetDefaultFileContent(); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null; }"), FixView(view)); @@ -20,7 +20,7 @@ namespace Umbraco.Tests.Templates public void Layout() { var view = ViewHelper.GetDefaultFileContent(layoutPageAlias: "Dharznoik"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = ""Dharznoik.cshtml""; }"), FixView(view)); @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Templates public void ClassName() { var view = ViewHelper.GetDefaultFileContent(modelClassName: "ClassName"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null; }"), FixView(view)); @@ -40,7 +40,7 @@ namespace Umbraco.Tests.Templates public void Namespace() { var view = ViewHelper.GetDefaultFileContent(modelNamespace: "Models"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null; }"), FixView(view)); @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Templates public void ClassNameAndNamespace() { var view = ViewHelper.GetDefaultFileContent(modelClassName: "ClassName", modelNamespace: "My.Models"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @using ContentModels = My.Models; @{ Layout = null; @@ -61,7 +61,7 @@ namespace Umbraco.Tests.Templates public void ClassNameAndNamespaceAndAlias() { var view = ViewHelper.GetDefaultFileContent(modelClassName: "ClassName", modelNamespace: "My.Models", modelNamespaceAlias: "MyModels"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @using MyModels = My.Models; @{ Layout = null; @@ -72,7 +72,7 @@ namespace Umbraco.Tests.Templates public void Combined() { var view = ViewHelper.GetDefaultFileContent(layoutPageAlias: "Dharznoik", modelClassName: "ClassName", modelNamespace: "My.Models", modelNamespaceAlias: "MyModels"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @using MyModels = My.Models; @{ Layout = ""Dharznoik.cshtml""; diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 0d8d4f48d0..00a698a4d1 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -401,6 +401,7 @@ + diff --git a/src/Umbraco.Web.UI/umbraco_client/PunyCode/punycode.min.js b/src/Umbraco.Web.UI/umbraco_client/PunyCode/punycode.min.js new file mode 100644 index 0000000000..e14359fbaf --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/PunyCode/punycode.min.js @@ -0,0 +1,2 @@ +/*! https://mths.be/punycode v1.3.2 by @mathias */ +!function(a){function b(a){throw RangeError(E[a])}function c(a,b){for(var c=a.length,d=[];c--;)d[c]=b(a[c]);return d}function d(a,b){var d=a.split("@"),e="";d.length>1&&(e=d[0]+"@",a=d[1]),a=a.replace(D,".");var f=a.split("."),g=c(f,b).join(".");return e+g}function e(a){for(var b,c,d=[],e=0,f=a.length;f>e;)b=a.charCodeAt(e++),b>=55296&&56319>=b&&f>e?(c=a.charCodeAt(e++),56320==(64512&c)?d.push(((1023&b)<<10)+(1023&c)+65536):(d.push(b),e--)):d.push(b);return d}function f(a){return c(a,function(a){var b="";return a>65535&&(a-=65536,b+=H(a>>>10&1023|55296),a=56320|1023&a),b+=H(a)}).join("")}function g(a){return 10>a-48?a-22:26>a-65?a-65:26>a-97?a-97:t}function h(a,b){return a+22+75*(26>a)-((0!=b)<<5)}function i(a,b,c){var d=0;for(a=c?G(a/x):a>>1,a+=G(a/b);a>F*v>>1;d+=t)a=G(a/F);return G(d+(F+1)*a/(a+w))}function j(a){var c,d,e,h,j,k,l,m,n,o,p=[],q=a.length,r=0,w=z,x=y;for(d=a.lastIndexOf(A),0>d&&(d=0),e=0;d>e;++e)a.charCodeAt(e)>=128&&b("not-basic"),p.push(a.charCodeAt(e));for(h=d>0?d+1:0;q>h;){for(j=r,k=1,l=t;h>=q&&b("invalid-input"),m=g(a.charCodeAt(h++)),(m>=t||m>G((s-r)/k))&&b("overflow"),r+=m*k,n=x>=l?u:l>=x+v?v:l-x,!(n>m);l+=t)o=t-n,k>G(s/o)&&b("overflow"),k*=o;c=p.length+1,x=i(r-j,c,0==j),G(r/c)>s-w&&b("overflow"),w+=G(r/c),r%=c,p.splice(r++,0,w)}return f(p)}function k(a){var c,d,f,g,j,k,l,m,n,o,p,q,r,w,x,B=[];for(a=e(a),q=a.length,c=z,d=0,j=y,k=0;q>k;++k)p=a[k],128>p&&B.push(H(p));for(f=g=B.length,g&&B.push(A);q>f;){for(l=s,k=0;q>k;++k)p=a[k],p>=c&&l>p&&(l=p);for(r=f+1,l-c>G((s-d)/r)&&b("overflow"),d+=(l-c)*r,c=l,k=0;q>k;++k)if(p=a[k],c>p&&++d>s&&b("overflow"),p==c){for(m=d,n=t;o=j>=n?u:n>=j+v?v:n-j,!(o>m);n+=t)x=m-o,w=t-o,B.push(H(h(o+x%w,0))),m=G(x/w);B.push(H(h(m,0))),j=i(d,r,f==g),d=0,++f}++d,++c}return B.join("")}function l(a){return d(a,function(a){return B.test(a)?j(a.slice(4).toLowerCase()):a})}function m(a){return d(a,function(a){return C.test(a)?"xn--"+k(a):a})}var n="object"==typeof exports&&exports&&!exports.nodeType&&exports,o="object"==typeof module&&module&&!module.nodeType&&module,p="object"==typeof global&&global;(p.global===p||p.window===p||p.self===p)&&(a=p);var q,r,s=2147483647,t=36,u=1,v=26,w=38,x=700,y=72,z=128,A="-",B=/^xn--/,C=/[^\x20-\x7E]/,D=/[\x2E\u3002\uFF0E\uFF61]/g,E={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},F=t-u,G=Math.floor,H=String.fromCharCode;if(q={version:"1.3.2",ucs2:{decode:e,encode:f},decode:j,encode:k,toASCII:m,toUnicode:l},"function"==typeof define&&"object"==typeof define.amd&&define.amd)define("punycode",function(){return q});else if(n&&o)if(module.exports==n)o.exports=q;else for(r in q)q.hasOwnProperty(r)&&(n[r]=q[r]);else a.punycode=q}(this); \ No newline at end of file diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index dd34dea6b5..1615dec94e 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -55,7 +55,7 @@ namespace Umbraco.Web /// /// Will render the preview badge when in preview mode which is not required ever unless the MVC page you are - /// using does not inherit from UmbracoTemplatePage + /// using does not inherit from UmbracoViewPage /// /// /// From 861e5908a736c6dcb5ed6236834f3a0c486cdf46 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 20 Apr 2018 10:35:21 +0200 Subject: [PATCH 14/97] U4-11253 New Chrome 66 trimStart function is overriding the Umbraco Core trimStart polyfill --- .../lib/umbraco/Extensions.js | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index b70a6b12bc..e8c6eda4bc 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -84,27 +84,21 @@ }; } - if (!String.prototype.trimStart) { - - /** trims the start of the string*/ - String.prototype.trimStart = function (str) { - if (this.startsWith(str)) { - return this.substring(str.length); - } - return this; - }; - } - - if (!String.prototype.trimEnd) { + /** trims the start of the string*/ + String.prototype.trimStart = function (str) { + if (this.startsWith(str)) { + return this.substring(str.length); + } + return this; + }; - /** trims the end of the string*/ - String.prototype.trimEnd = function (str) { - if (this.endsWith(str)) { - return this.substring(0, this.length - str.length); - } - return this; - }; - } + /** trims the end of the string*/ + String.prototype.trimEnd = function (str) { + if (this.endsWith(str)) { + return this.substring(0, this.length - str.length); + } + return this; + }; if (!String.prototype.utf8Encode) { @@ -332,4 +326,4 @@ } -})(); \ No newline at end of file +})(); From 4a0cd87a38aa82840dd7cc3260fca8e8c9165a3d Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 20 Apr 2018 10:51:28 +0200 Subject: [PATCH 15/97] U4-11253 New Chrome 66 trimStart function is overriding the Umbraco Core trimStart polyfill --- .../lib/umbraco/Extensions.js | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index b70a6b12bc..e8c6eda4bc 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -84,27 +84,21 @@ }; } - if (!String.prototype.trimStart) { - - /** trims the start of the string*/ - String.prototype.trimStart = function (str) { - if (this.startsWith(str)) { - return this.substring(str.length); - } - return this; - }; - } - - if (!String.prototype.trimEnd) { + /** trims the start of the string*/ + String.prototype.trimStart = function (str) { + if (this.startsWith(str)) { + return this.substring(str.length); + } + return this; + }; - /** trims the end of the string*/ - String.prototype.trimEnd = function (str) { - if (this.endsWith(str)) { - return this.substring(0, this.length - str.length); - } - return this; - }; - } + /** trims the end of the string*/ + String.prototype.trimEnd = function (str) { + if (this.endsWith(str)) { + return this.substring(0, this.length - str.length); + } + return this; + }; if (!String.prototype.utf8Encode) { @@ -332,4 +326,4 @@ } -})(); \ No newline at end of file +})(); From 7a181bd281b4874e6a7f2e31dbbad718efbebf02 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 20 Apr 2018 10:59:13 +0200 Subject: [PATCH 16/97] Bump version to 7.8.3 --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index e39fd33eb2..8a8fce9d31 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -11,5 +11,5 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyFileVersion("7.8.2")] -[assembly: AssemblyInformationalVersion("7.8.2")] \ No newline at end of file +[assembly: AssemblyFileVersion("7.8.3")] +[assembly: AssemblyInformationalVersion("7.8.3")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 1025d97c3d..9f3a8441bd 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration { public class UmbracoVersion { - private static readonly Version Version = new Version("7.8.2"); + private static readonly Version Version = new Version("7.8.3"); /// /// Gets the current version of Umbraco. diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 6b39c6ca05..d950779916 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -1024,9 +1024,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" True True - 7820 + 7830 / - http://localhost:7820 + http://localhost:7830 7800 / http://localhost:7800 From 27f6560a00c53ef389f7d808398298a372954101 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 20 Apr 2018 11:00:49 +0200 Subject: [PATCH 17/97] U4-11253 New Chrome 66 trimStart function is overriding the Umbraco Core trimStart polyfill --- .../lib/umbraco/Extensions.js | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index b70a6b12bc..e8c6eda4bc 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -84,27 +84,21 @@ }; } - if (!String.prototype.trimStart) { - - /** trims the start of the string*/ - String.prototype.trimStart = function (str) { - if (this.startsWith(str)) { - return this.substring(str.length); - } - return this; - }; - } - - if (!String.prototype.trimEnd) { + /** trims the start of the string*/ + String.prototype.trimStart = function (str) { + if (this.startsWith(str)) { + return this.substring(str.length); + } + return this; + }; - /** trims the end of the string*/ - String.prototype.trimEnd = function (str) { - if (this.endsWith(str)) { - return this.substring(0, this.length - str.length); - } - return this; - }; - } + /** trims the end of the string*/ + String.prototype.trimEnd = function (str) { + if (this.endsWith(str)) { + return this.substring(0, this.length - str.length); + } + return this; + }; if (!String.prototype.utf8Encode) { @@ -332,4 +326,4 @@ } -})(); \ No newline at end of file +})(); From f1a8da2876efeea2594a32513348c5d294e4d1a5 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 20 Apr 2018 11:02:29 +0200 Subject: [PATCH 18/97] Bumps version to 7.9.6 --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 71cecfdde2..87ea10b16c 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -11,5 +11,5 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyFileVersion("7.9.5")] -[assembly: AssemblyInformationalVersion("7.9.5")] \ No newline at end of file +[assembly: AssemblyFileVersion("7.9.6")] +[assembly: AssemblyInformationalVersion("7.9.6")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index b9b797aebb..87a5643816 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration { public class UmbracoVersion { - private static readonly Version Version = new Version("7.9.5"); + private static readonly Version Version = new Version("7.9.6"); /// /// Gets the current version of Umbraco. diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index b680b09c71..73bb070862 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -1023,9 +1023,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" True True - 7950 + 7960 / - http://localhost:7950 + http://localhost:7960 False False From a69019aea06ac3c16fbc70dac9a6bf9926721f06 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 21 Apr 2018 09:57:28 +0200 Subject: [PATCH 19/97] From int languageId to string culture --- .../Collections/CompositeStringStringKey.cs | 41 +++ src/Umbraco.Core/IO/MediaFileSystem.cs | 20 +- .../Media/UploadAutoFillProperties.cs | 62 ++-- src/Umbraco.Core/Models/Content.cs | 141 +++++---- src/Umbraco.Core/Models/ContentBase.cs | 64 ++-- src/Umbraco.Core/Models/ContentExtensions.cs | 8 +- src/Umbraco.Core/Models/ContentTypeBase.cs | 4 +- src/Umbraco.Core/Models/IContent.cs | 24 +- src/Umbraco.Core/Models/IContentBase.cs | 20 +- src/Umbraco.Core/Models/IContentTypeBase.cs | 2 +- src/Umbraco.Core/Models/Property.cs | 91 +++--- src/Umbraco.Core/Models/PropertyType.cs | 4 +- .../PublishedContent/IPublishedProperty.cs | 8 +- .../PublishedContent/PublishedPropertyBase.cs | 8 +- .../PublishedContent/RawValueProperty.cs | 16 +- .../Persistence/Factories/PropertyFactory.cs | 22 +- .../Repositories/ILanguageRepository.cs | 4 +- .../Implement/ContentRepositoryBase.cs | 12 +- .../Implement/DocumentRepository.cs | 20 +- .../Implement/LanguageRepository.cs | 20 +- .../Repositories/Implement/MediaRepository.cs | 8 +- .../Implement/MemberRepository.cs | 8 +- .../PropertyEditors/DataValueEditor.cs | 14 +- .../PropertyEditors/IDataValueEditor.cs | 2 +- src/Umbraco.Core/Services/IContentService.cs | 2 +- .../Services/ILocalizationService.cs | 2 +- .../Services/Implement/ContentService.cs | 8 +- .../Services/Implement/LocalizationService.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Composing/TypeLoaderTests.cs | 2 +- src/Umbraco.Tests/Models/VariationTests.cs | 92 +++--- .../Repositories/MediaRepositoryTest.cs | 2 +- .../Repositories/MemberRepositoryTest.cs | 2 +- .../Repositories/TagRepositoryTest.cs | 3 +- .../Repositories/UserRepositoryTest.cs | 2 +- .../Published/NestedContentTests.cs | 8 +- .../PublishedContentTestElements.cs | 8 +- .../PublishedContent/PublishedContentTests.cs | 2 +- .../Services/ContentServiceTests.cs | 276 +++++++++--------- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 4 +- src/Umbraco.Web/Editors/ContentController.cs | 33 ++- src/Umbraco.Web/Models/ContentExtensions.cs | 1 - .../Mapping/ContentPropertyBasicConverter.cs | 6 +- .../Models/Mapping/ContextMapper.cs | 10 +- .../Models/Mapping/VariationResolver.cs | 9 +- .../PropertyEditors/DateValueEditor.cs | 4 +- .../FileUploadPropertyEditor.cs | 10 +- .../ImageCropperPropertyEditor.cs | 8 +- .../ImageCropperPropertyValueEditor.cs | 4 +- .../MultipleTextStringPropertyEditor.cs | 4 +- .../NestedContentPropertyEditor.cs | 4 +- .../PublishValuesMultipleValueEditor.cs | 6 +- .../PropertyEditors/RichTextPropertyEditor.cs | 6 +- .../PropertyEditors/TextOnlyValueEditor.cs | 6 +- .../NuCache/DataSource/BTree.cs | 4 +- .../NuCache/DataSource/PropertyData.cs | 6 +- .../PublishedCache/NuCache/Property.cs | 57 ++-- .../NuCache/PublishedSnapshotService.cs | 2 +- .../PublishedElementPropertyBase.cs | 8 +- .../XmlPublishedCache/XmlPublishedProperty.cs | 8 +- .../PublishedContentPropertyExtension.cs | 14 +- src/Umbraco.Web/PublishedElementExtensions.cs | 75 +++-- .../WebApi/Binders/ContentItemBinder.cs | 2 +- src/Umbraco.Web/umbraco.presentation/page.cs | 8 +- 64 files changed, 708 insertions(+), 626 deletions(-) create mode 100644 src/Umbraco.Core/Collections/CompositeStringStringKey.cs diff --git a/src/Umbraco.Core/Collections/CompositeStringStringKey.cs b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs new file mode 100644 index 0000000000..11f123f2d0 --- /dev/null +++ b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs @@ -0,0 +1,41 @@ +using System; + +namespace Umbraco.Core.Collections +{ + /// + /// Represents a composite key of (string, string) for fast dictionaries. + /// + /// + /// The string parts of the key are case-insensitive. + /// Null is a valid value for both parts. + /// + public struct CompositeStringStringKey : IEquatable + { + private readonly string _key1; + private readonly string _key2; + + /// + /// Initializes a new instance of the struct. + /// + public CompositeStringStringKey(string key1, string key2) + { + _key1 = key1?.ToLowerInvariant() ?? "NULL"; + _key2 = key2?.ToLowerInvariant() ?? "NULL"; + } + + public bool Equals(CompositeStringStringKey other) + => _key2 == other._key2 && _key1 == other._key1; + + public override bool Equals(object obj) + => obj is CompositeStringStringKey other && _key2 == other._key2 && _key1 == other._key1; + + public override int GetHashCode() + => _key2.GetHashCode() * 31 + _key1.GetHashCode(); + + public static bool operator ==(CompositeStringStringKey key1, CompositeStringStringKey key2) + => key1._key2 == key2._key2 && key1._key1 == key2._key1; + + public static bool operator !=(CompositeStringStringKey key1, CompositeStringStringKey key2) + => key1._key2 != key2._key2 || key1._key1 != key2._key1; + } +} diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index b96b0d54bc..5534936a04 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -335,35 +335,35 @@ namespace Umbraco.Core.IO // fixme - what's below belongs to the upload property editor, not the media filesystem! - public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filename, Stream filestream, int? languageId = null, string segment = null) + public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { var property = GetProperty(content, propertyTypeAlias); - var oldpath = property.GetValue(languageId, segment) is string svalue ? GetRelativePath(svalue) : null; + var oldpath = property.GetValue(culture, segment) is string svalue ? GetRelativePath(svalue) : null; var filepath = StoreFile(content, property.PropertyType, filename, filestream, oldpath); - property.SetValue(GetUrl(filepath), languageId, segment); - SetUploadFile(content, property, filepath, filestream, languageId, segment); + property.SetValue(GetUrl(filepath), culture, segment); + SetUploadFile(content, property, filepath, filestream, culture, segment); } - public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filepath, int? languageId = null, string segment = null) + public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filepath, string culture = null, string segment = null) { var property = GetProperty(content, propertyTypeAlias); // fixme delete? - var oldpath = property.GetValue(languageId, segment) is string svalue ? GetRelativePath(svalue) : null; + var oldpath = property.GetValue(culture, segment) is string svalue ? GetRelativePath(svalue) : null; if (string.IsNullOrWhiteSpace(oldpath) == false && oldpath != filepath) DeleteFile(oldpath); - property.SetValue(GetUrl(filepath), languageId, segment); + property.SetValue(GetUrl(filepath), culture, segment); using (var filestream = OpenFile(filepath)) { - SetUploadFile(content, property, filepath, filestream, languageId, segment); + SetUploadFile(content, property, filepath, filestream, culture, segment); } } // sets a file for the FileUpload property editor // ie generates thumbnails and populates autofill properties - private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream, int? languageId = null, string segment = null) + private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream, string culture = null, string segment = null) { // will use filepath for extension, and filestream for length - UploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream, languageId, segment); + UploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream, culture, segment); } #endregion diff --git a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs index 3fdaf7b3d3..4ab2bf7a7c 100644 --- a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs @@ -41,9 +41,9 @@ namespace Umbraco.Core.Media /// /// The content item. /// The property type alias. - /// Variation language. + /// Variation language. /// Variation segment. - public void Reset(IContentBase content, string propertyTypeAlias, int? languageId, string segment) + public void Reset(IContentBase content, string propertyTypeAlias, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (propertyTypeAlias == null) throw new ArgumentNullException(nameof(propertyTypeAlias)); @@ -53,7 +53,7 @@ namespace Umbraco.Core.Media if (autoFillConfig == null) return; // nothing // reset - Reset(content, autoFillConfig, languageId, segment); + Reset(content, autoFillConfig, culture, segment); } /// @@ -61,14 +61,14 @@ namespace Umbraco.Core.Media /// /// The content item. /// The auto-fill configuration. - /// Variation language. + /// Variation language. /// Variation segment. - public void Reset(IContentBase content, IImagingAutoFillUploadField autoFillConfig, int? languageId, string segment) + public void Reset(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); - ResetProperties(content, autoFillConfig, languageId, segment); + ResetProperties(content, autoFillConfig, culture, segment); } /// @@ -77,9 +77,9 @@ namespace Umbraco.Core.Media /// The content item. /// The property type alias. /// The filesystem-relative filepath, or null to clear properties. - /// Variation language. + /// Variation language. /// Variation segment. - public void Populate(IContentBase content, string propertyTypeAlias, string filepath, int? languageId, string segment) + public void Populate(IContentBase content, string propertyTypeAlias, string filepath, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (propertyTypeAlias == null) throw new ArgumentNullException(nameof(propertyTypeAlias)); @@ -92,7 +92,7 @@ namespace Umbraco.Core.Media if (autoFillConfig == null) return; // nothing // populate - Populate(content, autoFillConfig, filepath, languageId, segment); + Populate(content, autoFillConfig, filepath, culture, segment); } /// @@ -102,9 +102,9 @@ namespace Umbraco.Core.Media /// The property type alias. /// The filesystem-relative filepath, or null to clear properties. /// The stream containing the file data. - /// Variation language. + /// Variation language. /// Variation segment. - public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream, int? languageId, string segment) + public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (propertyTypeAlias == null) throw new ArgumentNullException(nameof(propertyTypeAlias)); @@ -117,7 +117,7 @@ namespace Umbraco.Core.Media if (autoFillConfig == null) return; // nothing // populate - Populate(content, autoFillConfig, filepath, filestream, languageId, segment); + Populate(content, autoFillConfig, filepath, filestream, culture, segment); } /// @@ -127,9 +127,9 @@ namespace Umbraco.Core.Media /// The auto-fill configuration. /// The filesystem path to the uploaded file. /// The parameter is the path relative to the filesystem. - /// Variation language. + /// Variation language. /// Variation segment. - public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, int? languageId, string segment) + public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); @@ -137,7 +137,7 @@ namespace Umbraco.Core.Media // no file = reset, file = auto-fill if (filepath.IsNullOrWhiteSpace()) { - ResetProperties(content, autoFillConfig, languageId, segment); + ResetProperties(content, autoFillConfig, culture, segment); } else { @@ -148,13 +148,13 @@ namespace Umbraco.Core.Media { var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.'); var size = _mediaFileSystem.IsImageFile(extension) ? (Size?) _mediaFileSystem.GetDimensions(filestream) : null; - SetProperties(content, autoFillConfig, size, filestream.Length, extension, languageId, segment); + SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment); } } catch (Exception ex) { _logger.Error(typeof(UploadAutoFillProperties), $"Could not populate upload auto-fill properties for file \"{filepath}\".", ex); - ResetProperties(content, autoFillConfig, languageId, segment); + ResetProperties(content, autoFillConfig, culture, segment); } } } @@ -166,9 +166,9 @@ namespace Umbraco.Core.Media /// /// The filesystem-relative filepath, or null to clear properties. /// The stream containing the file data. - /// Variation language. + /// Variation language. /// Variation segment. - public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, int? languageId, string segment) + public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); @@ -176,50 +176,50 @@ namespace Umbraco.Core.Media // no file = reset, file = auto-fill if (filepath.IsNullOrWhiteSpace() || filestream == null) { - ResetProperties(content, autoFillConfig, languageId, segment); + ResetProperties(content, autoFillConfig, culture, segment); } else { var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.'); var size = _mediaFileSystem.IsImageFile(extension) ? (Size?)_mediaFileSystem.GetDimensions(filestream) : null; - SetProperties(content, autoFillConfig, size, filestream.Length, extension, languageId, segment); + SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment); } } - private static void SetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, Size? size, long length, string extension, int? languageId, string segment) + private static void SetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, Size? size, long length, string extension, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); if (content.Properties.Contains(autoFillConfig.WidthFieldAlias)) - content.Properties[autoFillConfig.WidthFieldAlias].SetValue(size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty, languageId, segment); + content.Properties[autoFillConfig.WidthFieldAlias].SetValue(size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.HeightFieldAlias)) - content.Properties[autoFillConfig.HeightFieldAlias].SetValue(size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty, languageId, segment); + content.Properties[autoFillConfig.HeightFieldAlias].SetValue(size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.LengthFieldAlias)) - content.Properties[autoFillConfig.LengthFieldAlias].SetValue(length, languageId, segment); + content.Properties[autoFillConfig.LengthFieldAlias].SetValue(length, culture, segment); if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias)) - content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(extension, languageId, segment); + content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(extension, culture, segment); } - private static void ResetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, int? languageId, string segment) + private static void ResetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); if (content.Properties.Contains(autoFillConfig.WidthFieldAlias)) - content.Properties[autoFillConfig.WidthFieldAlias].SetValue(string.Empty, languageId, segment); + content.Properties[autoFillConfig.WidthFieldAlias].SetValue(string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.HeightFieldAlias)) - content.Properties[autoFillConfig.HeightFieldAlias].SetValue(string.Empty, languageId, segment); + content.Properties[autoFillConfig.HeightFieldAlias].SetValue(string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.LengthFieldAlias)) - content.Properties[autoFillConfig.LengthFieldAlias].SetValue(string.Empty, languageId, segment); + content.Properties[autoFillConfig.LengthFieldAlias].SetValue(string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias)) - content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(string.Empty, languageId, segment); + content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(string.Empty, culture, segment); } } } diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 142d7540c2..1f106124b4 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Models private PublishedState _publishedState; private DateTime? _releaseDate; private DateTime? _expireDate; - private Dictionary _publishNames; + private Dictionary _publishNames; private static readonly Lazy Ps = new Lazy(); @@ -201,24 +201,24 @@ namespace Umbraco.Core.Models /// [IgnoreDataMember] - public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames; + public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames; /// - public string GetPublishName(int? languageId) + public string GetPublishName(string culture) { - if (languageId == null) return PublishName; + if (culture == null) return PublishName; if (_publishNames == null) return null; - return _publishNames.TryGetValue(languageId.Value, out var name) ? name : null; + return _publishNames.TryGetValue(culture, out var name) ? name : null; } // sets a publish name // internal for repositories - internal void SetPublishName(int? languageId, string name) + internal void SetPublishName(string culture, string name) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - if (languageId == null) + if (culture == null) { PublishName = name; return; @@ -227,22 +227,22 @@ namespace Umbraco.Core.Models // private method, assume that culture is valid if (_publishNames == null) - _publishNames = new Dictionary(); + _publishNames = new Dictionary(StringComparer.OrdinalIgnoreCase); - _publishNames[languageId.Value] = name; + _publishNames[culture] = name; } // clears a publish name - private void ClearPublishName(int? languageId) + private void ClearPublishName(string culture) { - if (languageId == null) + if (culture == null) { PublishName = null; return; } if (_publishNames == null) return; - _publishNames.Remove(languageId.Value); + _publishNames.Remove(culture); if (_publishNames.Count == 0) _publishNames = null; } @@ -255,12 +255,12 @@ namespace Umbraco.Core.Models } /// - public bool IsCultureAvailable(int? languageId) - => !string.IsNullOrWhiteSpace(GetName(languageId)); + public bool IsCultureAvailable(string culture) + => !string.IsNullOrWhiteSpace(GetName(culture)); /// - public bool IsCulturePublished(int? languageId) - => !string.IsNullOrWhiteSpace(GetPublishName(languageId)); + public bool IsCulturePublished(string culture) + => !string.IsNullOrWhiteSpace(GetPublishName(culture)); [IgnoreDataMember] public int PublishedVersionId { get; internal set; } @@ -275,57 +275,72 @@ namespace Umbraco.Core.Models if (ValidateAll().Any()) return false; - // property.PublishAllValues only deals with supported variations (if any) - foreach (var property in Properties) - property.PublishAllValues(); - // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - PublishName = Name; - foreach (var (languageId, name) in Names) - SetPublishName(languageId, name); + if (string.IsNullOrWhiteSpace(Name)) + throw new InvalidOperationException($"Cannot publish invariant culture without a name."); + PublishName = Name; + foreach (var (culture, name) in Names) + { + if (string.IsNullOrWhiteSpace(name)) + throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name."); + SetPublishName(culture, name); + } + // property.PublishAllValues only deals with supported variations (if any) + foreach (var property in Properties) + property.PublishAllValues(); + _publishedState = PublishedState.Publishing; return true; } /// - public virtual bool PublishValues(int? languageId = null, string segment = null) + public virtual bool PublishValues(string culture = null, string segment = null) { // the variation should be supported by the content type - ContentType.ValidateVariation(languageId, segment, throwIfInvalid: true); + ContentType.ValidateVariation(culture, segment, throwIfInvalid: true); // the values we want to publish should be valid - if (Validate(languageId, segment).Any()) + if (Validate(culture, segment).Any()) return false; - // property.PublishValue throws on invalid variation, so filter them out - foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(languageId, segment, throwIfInvalid: false))) - property.PublishValue(languageId, segment); - // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - SetPublishName(languageId, GetName(languageId)); + if (segment == null) + { + var name = GetName(culture); + if (string.IsNullOrWhiteSpace(name)) + throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name."); + SetPublishName(culture, name); + } + // property.PublishValue throws on invalid variation, so filter them out + foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(culture, segment, throwIfInvalid: false))) + property.PublishValue(culture, segment); + _publishedState = PublishedState.Publishing; return true; } /// - public virtual bool PublishCultureValues(int? languageId = null) + public virtual bool PublishCultureValues(string culture = null) { // the values we want to publish should be valid - if (ValidateCulture(languageId).Any()) + if (ValidateCulture(culture).Any()) return false; - // property.PublishCultureValues only deals with supported variations (if any) - foreach (var property in Properties) - property.PublishCultureValues(languageId); - // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - SetPublishName(languageId, GetName(languageId)); + var name = GetName(culture); + if (string.IsNullOrWhiteSpace(name)) + throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name."); + SetPublishName(culture, name); + // property.PublishCultureValues only deals with supported variations (if any) + foreach (var property in Properties) + property.PublishCultureValues(culture); + _publishedState = PublishedState.Publishing; return true; } @@ -345,32 +360,32 @@ namespace Umbraco.Core.Models } /// - public virtual void ClearPublishedValues(int? languageId = null, string segment = null) + public virtual void ClearPublishedValues(string culture = null, string segment = null) { // the variation should be supported by the content type - ContentType.ValidateVariation(languageId, segment, throwIfInvalid: true); + ContentType.ValidateVariation(culture, segment, throwIfInvalid: true); // property.ClearPublishedValue throws on invalid variation, so filter them out - foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(languageId, segment, throwIfInvalid: false))) - property.ClearPublishedValue(languageId, segment); + foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(culture, segment, throwIfInvalid: false))) + property.ClearPublishedValue(culture, segment); // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - ClearPublishName(languageId); + ClearPublishName(culture); _publishedState = PublishedState.Publishing; } /// - public virtual void ClearCulturePublishedValues(int? languageId = null) + public virtual void ClearCulturePublishedValues(string culture = null) { // property.ClearPublishedCultureValues only deals with supported variations (if any) foreach (var property in Properties) - property.ClearPublishedCultureValues(languageId); + property.ClearPublishedCultureValues(culture); // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - ClearPublishName(languageId); + ClearPublishName(culture); _publishedState = PublishedState.Publishing; } @@ -397,8 +412,8 @@ namespace Umbraco.Core.Models // clear all existing properties foreach (var property in Properties) foreach (var pvalue in property.Values) - if (property.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false)) - property.SetValue(null, pvalue.LanguageId, pvalue.Segment); + if (property.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false)) + property.SetValue(null, pvalue.Culture, pvalue.Segment); // copy other properties var otherProperties = other.Properties; @@ -407,10 +422,10 @@ namespace Umbraco.Core.Models var alias = otherProperty.PropertyType.Alias; foreach (var pvalue in otherProperty.Values) { - if (!otherProperty.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false)) + if (!otherProperty.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false)) continue; var value = published ? pvalue.PublishedValue : pvalue.EditedValue; - SetValue(alias, value, pvalue.LanguageId, pvalue.Segment); + SetValue(alias, value, pvalue.Culture, pvalue.Segment); } } @@ -422,7 +437,7 @@ namespace Umbraco.Core.Models } /// - public virtual void CopyValues(IContent other, int? languageId = null, string segment = null) + public virtual void CopyValues(IContent other, string culture = null, string segment = null) { if (other.ContentTypeId != ContentTypeId) throw new InvalidOperationException("Cannot copy values from a different content type."); @@ -437,31 +452,31 @@ namespace Umbraco.Core.Models // clear all existing properties foreach (var property in Properties) { - if (!property.PropertyType.ValidateVariation(languageId, segment, false)) + if (!property.PropertyType.ValidateVariation(culture, segment, false)) continue; foreach (var pvalue in property.Values) - if (pvalue.LanguageId == languageId && pvalue.Segment == segment) - property.SetValue(null, pvalue.LanguageId, pvalue.Segment); + if (pvalue.Culture.InvariantEquals(culture) && pvalue.Segment.InvariantEquals(segment)) + property.SetValue(null, pvalue.Culture, pvalue.Segment); } // copy other properties var otherProperties = other.Properties; foreach (var otherProperty in otherProperties) { - if (!otherProperty.PropertyType.ValidateVariation(languageId, segment, false)) + if (!otherProperty.PropertyType.ValidateVariation(culture, segment, false)) continue; var alias = otherProperty.PropertyType.Alias; - SetValue(alias, otherProperty.GetValue(languageId, segment, published), languageId, segment); + SetValue(alias, otherProperty.GetValue(culture, segment, published), culture, segment); } // copy name - SetName(languageId, other.GetName(languageId)); + SetName(culture, other.GetName(culture)); } /// - public virtual void CopyCultureValues(IContent other, int? languageId = null) + public virtual void CopyCultureValues(IContent other, string culture = null) { if (other.ContentTypeId != ContentTypeId) throw new InvalidOperationException("Cannot copy values from a different content type."); @@ -473,8 +488,8 @@ namespace Umbraco.Core.Models // clear all existing properties foreach (var property in Properties) foreach (var pvalue in property.Values) - if (pvalue.LanguageId == languageId && property.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false)) - property.SetValue(null, pvalue.LanguageId, pvalue.Segment); + if (pvalue.Culture.InvariantEquals(culture) && property.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false)) + property.SetValue(null, pvalue.Culture, pvalue.Segment); // copy other properties var otherProperties = other.Properties; @@ -483,15 +498,15 @@ namespace Umbraco.Core.Models var alias = otherProperty.PropertyType.Alias; foreach (var pvalue in otherProperty.Values) { - if (pvalue.LanguageId != languageId || !otherProperty.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false)) + if (pvalue.Culture != culture || !otherProperty.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false)) continue; var value = published ? pvalue.PublishedValue : pvalue.EditedValue; - SetValue(alias, value, pvalue.LanguageId, pvalue.Segment); + SetValue(alias, value, pvalue.Culture, pvalue.Segment); } } // copy name - SetName(languageId, other.GetName(languageId)); + SetName(culture, other.GetName(culture)); } /// diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 8203cba985..32a26f9dc0 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -18,14 +18,14 @@ namespace Umbraco.Core.Models [DebuggerDisplay("Id: {Id}, Name: {Name}, ContentType: {ContentTypeBase.Alias}")] public abstract class ContentBase : TreeEntityBase, IContentBase { - protected static readonly Dictionary NoNames = new Dictionary(); + protected static readonly Dictionary NoNames = new Dictionary(); private static readonly Lazy Ps = new Lazy(); private int _contentTypeId; protected IContentTypeComposition ContentTypeBase; private int _writerId; private PropertyCollection _properties; - private Dictionary _names; + private Dictionary _names; /// /// Initializes a new instance of the class. @@ -67,7 +67,7 @@ namespace Umbraco.Core.Models public readonly PropertyInfo DefaultContentTypeIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeId); public readonly PropertyInfo PropertyCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); public readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo(x => x.WriterId); - public readonly PropertyInfo NamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.Names); + public readonly PropertyInfo NamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.Names); } protected void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -123,26 +123,26 @@ namespace Umbraco.Core.Models /// [DataMember] - public virtual IReadOnlyDictionary Names + public virtual IReadOnlyDictionary Names { get => _names ?? NoNames; set { - foreach (var (languageId, name) in value) - SetName(languageId, name); + foreach (var (culture, name) in value) + SetName(culture, name); } } /// - public virtual void SetName(int? languageId, string name) + public virtual void SetName(string culture, string name) { if (string.IsNullOrWhiteSpace(name)) { - ClearName(languageId); + ClearName(culture); return; } - if (languageId == null) + if (culture == null) { Name = name; return; @@ -152,22 +152,22 @@ namespace Umbraco.Core.Models throw new NotSupportedException("Content type does not support varying name by culture."); if (_names == null) - _names = new Dictionary(); + _names = new Dictionary(StringComparer.OrdinalIgnoreCase); - _names[languageId.Value] = name; + _names[culture] = name; OnPropertyChanged(Ps.Value.NamesSelector); } - private void ClearName(int? languageId) + private void ClearName(string culture) { - if (languageId == null) + if (culture == null) { Name = null; return; } if (_names == null) return; - _names.Remove(languageId.Value); + _names.Remove(culture); if (_names.Count == 0) _names = null; } @@ -179,11 +179,11 @@ namespace Umbraco.Core.Models } /// - public virtual string GetName(int? languageId) + public virtual string GetName(string culture) { - if (languageId == null) return Name; + if (culture == null) return Name; if (_names == null) return null; - return _names.TryGetValue(languageId.Value, out var name) ? name : null; + return _names.TryGetValue(culture, out var name) ? name : null; } /// @@ -207,29 +207,29 @@ namespace Umbraco.Core.Models => Properties.Contains(propertyTypeAlias); /// - public virtual object GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false) + public virtual object GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false) { return Properties.TryGetValue(propertyTypeAlias, out var property) - ? property.GetValue(languageId, segment, published) + ? property.GetValue(culture, segment, published) : null; } /// - public virtual TValue GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false) + public virtual TValue GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false) { if (!Properties.TryGetValue(propertyTypeAlias, out var property)) return default; - var convertAttempt = property.GetValue(languageId, segment, published).TryConvertTo(); + var convertAttempt = property.GetValue(culture, segment, published).TryConvertTo(); return convertAttempt.Success ? convertAttempt.Result : default; } /// - public virtual void SetValue(string propertyTypeAlias, object value, int? languageId = null, string segment = null) + public virtual void SetValue(string propertyTypeAlias, object value, string culture = null, string segment = null) { if (Properties.Contains(propertyTypeAlias)) { - Properties[propertyTypeAlias].SetValue(value, languageId, segment); + Properties[propertyTypeAlias].SetValue(value, culture, segment); return; } @@ -238,7 +238,7 @@ namespace Umbraco.Core.Models throw new InvalidOperationException($"No PropertyType exists with the supplied alias \"{propertyTypeAlias}\"."); var property = propertyType.CreateProperty(); - property.SetValue(value, languageId, segment); + property.SetValue(value, culture, segment); Properties.Add(property); } @@ -249,17 +249,17 @@ namespace Umbraco.Core.Models /// /// Sets the posted file value of a property. /// - public virtual void SetValue(string propertyTypeAlias, HttpPostedFile value, int? languageId = null, string segment = null) + public virtual void SetValue(string propertyTypeAlias, HttpPostedFile value, string culture = null, string segment = null) { - ContentExtensions.SetValue(this, propertyTypeAlias, new HttpPostedFileWrapper(value), languageId, segment); + ContentExtensions.SetValue(this, propertyTypeAlias, new HttpPostedFileWrapper(value), culture, segment); } /// /// Sets the posted file value of a property. /// - public virtual void SetValue(string propertyTypeAlias, HttpPostedFileBase value, int? languageId = null, string segment = null) + public virtual void SetValue(string propertyTypeAlias, HttpPostedFileBase value, string culture = null, string segment = null) { - ContentExtensions.SetValue(this, propertyTypeAlias, value, languageId, segment); + ContentExtensions.SetValue(this, propertyTypeAlias, value, culture, segment); } #endregion @@ -271,14 +271,14 @@ namespace Umbraco.Core.Models return Properties.Where(x => !x.IsAllValid()).ToArray(); } - public virtual Property[] Validate(int? languageId = null, string segment = null) + public virtual Property[] Validate(string culture = null, string segment = null) { - return Properties.Where(x => !x.IsValid(languageId, segment)).ToArray(); + return Properties.Where(x => !x.IsValid(culture, segment)).ToArray(); } - public virtual Property[] ValidateCulture(int? languageId = null) + public virtual Property[] ValidateCulture(string culture = null) { - return Properties.Where(x => !x.IsCultureValid(languageId)).ToArray(); + return Properties.Where(x => !x.IsCultureValid(culture)).ToArray(); } #endregion diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 5b7e825d68..0940675346 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -284,7 +284,7 @@ namespace Umbraco.Core.Models /// /// Sets the posted file value of a property. /// - public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value, int? languageId = null, string segment = null) + public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value, string culture = null, string segment = null) { // ensure we get the filename without the path in IE in intranet mode // http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie @@ -303,7 +303,7 @@ namespace Umbraco.Core.Models if (string.IsNullOrWhiteSpace(filename)) return; filename = filename.ToLower(); // fixme - er... why? - MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, value.InputStream, languageId, segment); + MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, value.InputStream, culture, segment); } /// @@ -312,7 +312,7 @@ namespace Umbraco.Core.Models /// This really is for FileUpload fields only, and should be obsoleted. For anything else, /// you need to store the file by yourself using Store and then figure out /// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself. - public static void SetValue(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, int? languageId = null, string segment = null) + public static void SetValue(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { if (filename == null || filestream == null) return; @@ -321,7 +321,7 @@ namespace Umbraco.Core.Models if (string.IsNullOrWhiteSpace(filename)) return; filename = filename.ToLower(); // fixme - er... why? - MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, filestream, languageId, segment); + MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, filestream, culture, segment); } /// diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index b686b97108..7d0ffea31e 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -204,10 +204,10 @@ namespace Umbraco.Core.Models /// /// Validates that a variation is valid for the content type. /// - public bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid) + public bool ValidateVariation(string culture, string segment, bool throwIfInvalid) { ContentVariation variation; - if (languageId.HasValue) + if (culture != null) { variation = segment != null ? ContentVariation.CultureSegment diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 3f7a335620..3df81f7314 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -59,7 +59,7 @@ namespace Umbraco.Core.Models /// Gets the date and time the content was published. /// DateTime? PublishDate { get; } - + /// /// Gets or sets the date and time the content item should be published. /// @@ -87,7 +87,7 @@ namespace Umbraco.Core.Models /// A culture becomes available whenever the content name for this culture is /// non-null, and it becomes unavailable whenever the content name is null. /// - bool IsCultureAvailable(int? languageId); + bool IsCultureAvailable(string culture); /// /// Gets a value indicating whether a given culture is published. @@ -97,17 +97,17 @@ namespace Umbraco.Core.Models /// and the content published name for this culture is non-null. It becomes non-published /// whenever values for this culture are unpublished. /// - bool IsCulturePublished(int? languageId); + bool IsCulturePublished(string culture); /// /// Gets the name of the published version of the content for a given culture. /// /// /// When editing the content, the name can change, but this will not until the content is published. - /// When is null, gets the invariant + /// When is null, gets the invariant /// language, which is the value of the property. /// - string GetPublishName(int? languageId); + string GetPublishName(string culture); /// /// Gets the published names of the content. @@ -116,7 +116,7 @@ namespace Umbraco.Core.Models /// Because a dictionary key cannot be null this cannot get the invariant /// name, which must be get via the property. /// - IReadOnlyDictionary PublishNames { get; } + IReadOnlyDictionary PublishNames { get; } // fixme - these two should move to some kind of service @@ -159,7 +159,7 @@ namespace Umbraco.Core.Models /// The document must then be published via the content service. /// Values are not published if they are not valid. /// - bool PublishValues(int? languageId = null, string segment = null); + bool PublishValues(string culture = null, string segment = null); /// /// Publishes the culture/any values. @@ -169,7 +169,7 @@ namespace Umbraco.Core.Models /// The document must then be published via the content service. /// Values are not published if they are not valie. /// - bool PublishCultureValues(int? languageId = null); + bool PublishCultureValues(string culture = null); /// /// Clears all published values. @@ -179,12 +179,12 @@ namespace Umbraco.Core.Models /// /// Clears published values. /// - void ClearPublishedValues(int? languageId = null, string segment = null); + void ClearPublishedValues(string culture = null, string segment = null); /// /// Clears the culture/any published values. /// - void ClearCulturePublishedValues(int? languageId = null); + void ClearCulturePublishedValues(string culture = null); /// /// Copies values from another document. @@ -194,11 +194,11 @@ namespace Umbraco.Core.Models /// /// Copies values from another document. /// - void CopyValues(IContent other, int? languageId = null, string segment = null); + void CopyValues(IContent other, string culture = null, string segment = null); /// /// Copies culture/any values from another document. /// - void CopyCultureValues(IContent other, int? languageId = null); + void CopyCultureValues(IContent other, string culture = null); } } diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index aee8acd35e..f8bb0e6985 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -31,19 +31,19 @@ namespace Umbraco.Core.Models /// Sets the name of the content item for a specified language. /// /// - /// When is null, sets the invariant + /// When is null, sets the invariant /// language, which sets the property. /// - void SetName(int? languageId, string value); + void SetName(string culture, string value); /// /// Gets the name of the content item for a specified language. /// /// - /// When is null, gets the invariant + /// When is null, gets the invariant /// language, which is the value of the property. /// - string GetName(int? languageId); + string GetName(string culture); /// /// Gets or sets the names of the content item. @@ -52,7 +52,7 @@ namespace Umbraco.Core.Models /// Because a dictionary key cannot be null this cannot get nor set the invariant /// name, which must be get or set via the property. /// - IReadOnlyDictionary Names { get; set; } + IReadOnlyDictionary Names { get; set; } /// /// List of properties, which make up all the data available for this Content object @@ -82,17 +82,17 @@ namespace Umbraco.Core.Models /// /// Gets the value of a Property /// - object GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false); + object GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false); /// /// Gets the typed value of a Property /// - TValue GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false); + TValue GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false); /// /// Sets the (edited) value of a Property /// - void SetValue(string propertyTypeAlias, object value, int? languageId = null, string segment = null); + void SetValue(string propertyTypeAlias, object value, string culture = null, string segment = null); /// /// Gets a value indicating whether the content and all its properties values are valid. @@ -102,11 +102,11 @@ namespace Umbraco.Core.Models /// /// Gets a value indicating whether the content and its properties values are valid. /// - Property[] Validate(int? languageId = null, string segment = null); + Property[] Validate(string culture = null, string segment = null); /// /// Gets a value indicating whether the content and its culture/any properties values are valid. /// - Property[] ValidateCulture(int? languageId = null); + Property[] ValidateCulture(string culture = null); } } diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs index 68e5923d66..7c8cc8eafe 100644 --- a/src/Umbraco.Core/Models/IContentTypeBase.cs +++ b/src/Umbraco.Core/Models/IContentTypeBase.cs @@ -51,7 +51,7 @@ namespace Umbraco.Core.Models /// /// Validates that a variation is valid for the content type. /// - bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid); + bool ValidateVariation(string culture, string segment, bool throwIfInvalid); /// /// Gets or Sets a list of integer Ids of the ContentTypes allowed under the ContentType diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 9e3f5fc2e0..709c37daaa 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Models { private List _values = new List(); private PropertyValue _pvalue; - private Dictionary _vvalues; + private Dictionary _vvalues; private static readonly Lazy Ps = new Lazy(); @@ -38,9 +38,14 @@ namespace Umbraco.Core.Models public class PropertyValue { + private string _culture; private string _segment; - public int? LanguageId { get; internal set; } + public string Culture + { + get => _culture; + internal set => _culture = value?.ToLowerInvariant(); + } public string Segment { get => _segment; @@ -50,7 +55,7 @@ namespace Umbraco.Core.Models public object PublishedValue { get; internal set; } public PropertyValue Clone() - => new PropertyValue { LanguageId = LanguageId, _segment = _segment, PublishedValue = PublishedValue, EditedValue = EditedValue }; + => new PropertyValue { _culture = _culture, _segment = _segment, PublishedValue = PublishedValue, EditedValue = EditedValue }; } // ReSharper disable once ClassNeverInstantiated.Local @@ -96,10 +101,10 @@ namespace Umbraco.Core.Models { // make sure we filter out invalid variations // make sure we leave _vvalues null if possible - _values = value.Where(x => PropertyType.ValidateVariation(x.LanguageId, x.Segment, false)).ToList(); - _pvalue = _values.FirstOrDefault(x => !x.LanguageId.HasValue && x.Segment == null); + _values = value.Where(x => PropertyType.ValidateVariation(x.Culture, x.Segment, false)).ToList(); + _pvalue = _values.FirstOrDefault(x => x.Culture == null && x.Segment == null); _vvalues = _values.Count > (_pvalue == null ? 0 : 1) - ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeIntStringKey(x.LanguageId, x.Segment), x => x) + ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeStringStringKey(x.Culture, x.Segment), x => x) : null; } } @@ -128,12 +133,12 @@ namespace Umbraco.Core.Models /// /// Gets the value. /// - public object GetValue(int? languageId = null, string segment = null, bool published = false) + public object GetValue(string culture = null, string segment = null, bool published = false) { - if (!PropertyType.ValidateVariation(languageId, segment, false)) return null; - if (!languageId.HasValue && segment == null) return GetPropertyValue(_pvalue, published); + if (!PropertyType.ValidateVariation(culture, segment, false)) return null; + if (culture == null && segment == null) return GetPropertyValue(_pvalue, published); if (_vvalues == null) return null; - return _vvalues.TryGetValue(new CompositeIntStringKey(languageId, segment), out var pvalue) + return _vvalues.TryGetValue(new CompositeStringStringKey(culture, segment), out var pvalue) ? GetPropertyValue(pvalue, published) : null; } @@ -159,7 +164,7 @@ namespace Umbraco.Core.Models if (_vvalues != null) { var pvalues = _vvalues - .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false)) + .Where(x => PropertyType.ValidateVariation(x.Value.Culture, x.Value.Segment, false)) .Select(x => x.Value); foreach (var pvalue in pvalues) PublishPropertyValue(pvalue); @@ -168,29 +173,29 @@ namespace Umbraco.Core.Models // internal - must be invoked by the content item // does *not* validate the value - content item must validate first - internal void PublishValue(int? languageId = null, string segment = null) + internal void PublishValue(string culture = null, string segment = null) { - PropertyType.ValidateVariation(languageId, segment, true); + PropertyType.ValidateVariation(culture, segment, true); - (var pvalue, _) = GetPValue(languageId, segment, false); + (var pvalue, _) = GetPValue(culture, segment, false); if (pvalue == null) return; PublishPropertyValue(pvalue); } // internal - must be invoked by the content item // does *not* validate the value - content item must validate first - internal void PublishCultureValues(int? languageId = null) + internal void PublishCultureValues(string culture = null) { // if invariant and invariant-neutral is supported, publish invariant-neutral - if (!languageId.HasValue && PropertyType.ValidateVariation(null, null, false)) + if (culture == null && PropertyType.ValidateVariation(null, null, false)) PublishPropertyValue(_pvalue); // publish everything not invariant-neutral that matches the culture and is supported if (_vvalues != null) { var pvalues = _vvalues - .Where(x => x.Value.LanguageId == languageId) - .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false)) + .Where(x => x.Value.Culture.InvariantEquals(culture)) + .Where(x => PropertyType.ValidateVariation(culture, x.Value.Segment, false)) .Select(x => x.Value); foreach (var pvalue in pvalues) PublishPropertyValue(pvalue); @@ -206,7 +211,7 @@ namespace Umbraco.Core.Models if (_vvalues != null) { var pvalues = _vvalues - .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false)) + .Where(x => PropertyType.ValidateVariation(x.Value.Culture, x.Value.Segment, false)) .Select(x => x.Value); foreach (var pvalue in pvalues) ClearPublishedPropertyValue(pvalue); @@ -214,25 +219,25 @@ namespace Umbraco.Core.Models } // internal - must be invoked by the content item - internal void ClearPublishedValue(int? languageId = null, string segment = null) + internal void ClearPublishedValue(string culture = null, string segment = null) { - PropertyType.ValidateVariation(languageId, segment, true); - (var pvalue, _) = GetPValue(languageId, segment, false); + PropertyType.ValidateVariation(culture, segment, true); + (var pvalue, _) = GetPValue(culture, segment, false); if (pvalue == null) return; ClearPublishedPropertyValue(pvalue); } // internal - must be invoked by the content item - internal void ClearPublishedCultureValues(int? languageId = null) + internal void ClearPublishedCultureValues(string culture = null) { - if (!languageId.HasValue && PropertyType.ValidateVariation(null, null, false)) + if (culture == null && PropertyType.ValidateVariation(null, null, false)) ClearPublishedPropertyValue(_pvalue); if (_vvalues != null) { var pvalues = _vvalues - .Where(x => x.Value.LanguageId == languageId) - .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false)) + .Where(x => x.Value.Culture.InvariantEquals(culture)) + .Where(x => PropertyType.ValidateVariation(culture, x.Value.Segment, false)) .Select(x => x.Value); foreach (var pvalue in pvalues) ClearPublishedPropertyValue(pvalue); @@ -264,10 +269,10 @@ namespace Umbraco.Core.Models /// /// Sets a value. /// - public void SetValue(object value, int? languageId = null, string segment = null) + public void SetValue(object value, string culture = null, string segment = null) { - PropertyType.ValidateVariation(languageId, segment, true); - (var pvalue, var change) = GetPValue(languageId, segment, true); + PropertyType.ValidateVariation(culture, segment, true); + (var pvalue, var change) = GetPValue(culture, segment, true); var origValue = pvalue.EditedValue; var setValue = PropertyType.ConvertAssignedValue(value); @@ -278,9 +283,9 @@ namespace Umbraco.Core.Models } // bypasses all changes detection and is the *only* way to set the published value - internal void FactorySetValue(int? languageId, string segment, bool published, object value) + internal void FactorySetValue(string culture, string segment, bool published, object value) { - (var pvalue, _) = GetPValue(languageId, segment, true); + (var pvalue, _) = GetPValue(culture, segment, true); if (published && PropertyType.IsPublishing) pvalue.PublishedValue = value; @@ -301,24 +306,24 @@ namespace Umbraco.Core.Models return (_pvalue, change); } - private (PropertyValue, bool) GetPValue(int? languageId, string segment, bool create) + private (PropertyValue, bool) GetPValue(string culture, string segment, bool create) { - if (!languageId.HasValue && segment == null) + if (culture == null && segment == null) return GetPValue(create); var change = false; if (_vvalues == null) { if (!create) return (null, false); - _vvalues = new Dictionary(); + _vvalues = new Dictionary(); change = true; } - var k = new CompositeIntStringKey(languageId, segment); + var k = new CompositeStringStringKey(culture, segment); if (!_vvalues.TryGetValue(k, out var pvalue)) { if (!create) return (null, false); pvalue = _vvalues[k] = new PropertyValue(); - pvalue.LanguageId = languageId; + pvalue.Culture = culture; pvalue.Segment = segment; _values.Add(pvalue); change = true; @@ -343,7 +348,7 @@ namespace Umbraco.Core.Models if (_vvalues == null) return true; var pvalues = _vvalues - .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false)) + .Where(x => PropertyType.ValidateVariation(x.Value.Culture, x.Value.Segment, false)) .Select(x => x.Value) .ToArray(); @@ -354,11 +359,11 @@ namespace Umbraco.Core.Models /// Gets a value indicating whether the culture/any values are valid. /// /// An invalid value can be saved, but only valid values can be published. - public bool IsCultureValid(int? languageId) + public bool IsCultureValid(string culture) { // culture-neutral is supported, validate culture-neutral // includes mandatory validation - if (PropertyType.ValidateVariation(languageId, null, false) && !IsValidValue(GetValue(languageId))) + if (PropertyType.ValidateVariation(culture, null, false) && !IsValidValue(GetValue(culture))) return false; // either culture-neutral is not supported, or it is valid @@ -368,8 +373,8 @@ namespace Umbraco.Core.Models if (_vvalues == null) return true; var pvalues = _vvalues - .Where(x => x.Value.LanguageId == languageId) - .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false)) + .Where(x => x.Value.Culture.InvariantEquals(culture)) + .Where(x => PropertyType.ValidateVariation(culture, x.Value.Segment, false)) .Select(x => x.Value) .ToArray(); @@ -380,10 +385,10 @@ namespace Umbraco.Core.Models /// Gets a value indicating whether the value is valid. /// /// An invalid value can be saved, but only valid values can be published. - public bool IsValid(int? languageId = null, string segment = null) + public bool IsValid(string culture = null, string segment = null) { // single value -> validates mandatory - return IsValidValue(GetValue(languageId, segment)); + return IsValidValue(GetValue(culture, segment)); } /// diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 8c5e318719..b34eca7c57 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -226,10 +226,10 @@ namespace Umbraco.Core.Models /// /// Validates that a variation is valid for the property type. /// - public bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid) + public bool ValidateVariation(string culture, string segment, bool throwIfInvalid) { ContentVariation variation; - if (languageId.HasValue) + if (culture != null) { variation = segment != null ? ContentVariation.CultureSegment diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs index 24c654604c..9d2cca3e6d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs @@ -21,7 +21,7 @@ /// Other caches that get their raw value from the database would consider that a property has "no /// value" if it is missing, null, or an empty string (including whitespace-only). /// - bool HasValue(int? languageId = null, string segment = null); + bool HasValue(string culture = null, string segment = null); /// /// Gets the source value of the property. @@ -35,7 +35,7 @@ /// If you're using that value, you're probably wrong, unless you're doing some internal /// Umbraco stuff. /// - object GetSourceValue(int? languageId = null, string segment = null); + object GetSourceValue(string culture = null, string segment = null); /// /// Gets the object value of the property. @@ -45,7 +45,7 @@ /// It can be null, or any type of CLR object. /// It has been fully prepared and processed by the appropriate converter. /// - object GetValue(int? languageId = null, string segment = null); + object GetValue(string culture = null, string segment = null); /// /// Gets the XPath value of the property. @@ -55,6 +55,6 @@ /// It must be either null, or a string, or an XPathNavigator. /// It has been fully prepared and processed by the appropriate converter. /// - object GetXPathValue(int? languageId = null, string segment = null); + object GetXPathValue(string culture = null, string segment = null); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs index 918bdb86e4..7e2a5b5498 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs @@ -53,15 +53,15 @@ namespace Umbraco.Core.Models.PublishedContent public string Alias => PropertyType.Alias; /// - public abstract bool HasValue(int? languageId = null, string segment = null); + public abstract bool HasValue(string culture = null, string segment = null); /// - public abstract object GetSourceValue(int? languageId = null, string segment = null); + public abstract object GetSourceValue(string culture = null, string segment = null); /// - public abstract object GetValue(int? languageId = null, string segment = null); + public abstract object GetValue(string culture = null, string segment = null); /// - public abstract object GetXPathValue(int? languageId = null, string segment = null); + public abstract object GetXPathValue(string culture = null, string segment = null); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs index f938880060..e20d8cb49c 100644 --- a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs @@ -20,20 +20,20 @@ namespace Umbraco.Core.Models.PublishedContent private readonly Lazy _objectValue; private readonly Lazy _xpathValue; - public override object GetSourceValue(int? languageId = null, string segment = null) - => languageId == null & segment == null ? _sourceValue : null; + public override object GetSourceValue(string culture = null, string segment = null) + => culture == null & segment == null ? _sourceValue : null; - public override bool HasValue(int? languageId = null, string segment = null) + public override bool HasValue(string culture = null, string segment = null) { - var sourceValue = GetSourceValue(languageId, segment); + var sourceValue = GetSourceValue(culture, segment); return sourceValue is string s ? !string.IsNullOrWhiteSpace(s) : sourceValue != null; } - public override object GetValue(int? languageId = null, string segment = null) - => languageId == null & segment == null ? _objectValue.Value : null; + public override object GetValue(string culture = null, string segment = null) + => culture == null & segment == null ? _objectValue.Value : null; - public override object GetXPathValue(int? languageId = null, string segment = null) - => languageId == null & segment == null ? _xpathValue.Value : null; + public override object GetXPathValue(string culture = null, string segment = null) + => culture == null & segment == null ? _xpathValue.Value : null; public RawValueProperty(PublishedPropertyType propertyType, IPublishedElement content, object sourceValue, bool isPreviewing = false) : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index 4e2789bab7..10f2918204 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Services; namespace Umbraco.Core.Persistence.Factories { internal static class PropertyFactory { - public static IEnumerable BuildEntities(PropertyType[] propertyTypes, IReadOnlyCollection dtos, int publishedVersionId) + public static IEnumerable BuildEntities(PropertyType[] propertyTypes, IReadOnlyCollection dtos, int publishedVersionId, ILanguageRepository languageRepository) { var properties = new List(); var xdtos = dtos.GroupBy(x => x.PropertyTypeId).ToDictionary(x => x.Key, x => (IEnumerable) x); @@ -26,7 +28,7 @@ namespace Umbraco.Core.Persistence.Factories if (xdtos.TryGetValue(propertyType.Id, out var propDtos)) { foreach (var propDto in propDtos) - property.FactorySetValue(propDto.LanguageId, propDto.Segment, propDto.VersionId == publishedVersionId, propDto.Value); + property.FactorySetValue(languageRepository.GetIsoCodeById(propDto.LanguageId), propDto.Segment, propDto.VersionId == publishedVersionId, propDto.Value); } property.ResetDirtyProperties(false); @@ -41,12 +43,12 @@ namespace Umbraco.Core.Persistence.Factories return properties; } - private static PropertyDataDto BuildDto(int versionId, Property property, int? nLanguageId, string segment, object value) + private static PropertyDataDto BuildDto(int versionId, Property property, int? languageId, string segment, object value) { var dto = new PropertyDataDto { VersionId = versionId, PropertyTypeId = property.PropertyTypeId }; - if (nLanguageId.HasValue) - dto.LanguageId = nLanguageId; + if (languageId.HasValue) + dto.LanguageId = languageId; if (segment != null) dto.Segment = segment; @@ -88,7 +90,7 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - public static IEnumerable BuildDtos(int currentVersionId, int publishedVersionId, IEnumerable properties, out bool edited) + public static IEnumerable BuildDtos(int currentVersionId, int publishedVersionId, IEnumerable properties, ILanguageRepository languageRepository, out bool edited) { var propertyDataDtos = new List(); edited = false; @@ -102,15 +104,15 @@ namespace Umbraco.Core.Persistence.Factories { // deal with published value if (propertyValue.PublishedValue != null && publishedVersionId > 0) - propertyDataDtos.Add(BuildDto(publishedVersionId, property, propertyValue.LanguageId, propertyValue.Segment, propertyValue.PublishedValue)); + propertyDataDtos.Add(BuildDto(publishedVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.PublishedValue)); // deal with edit value if (propertyValue.EditedValue != null) - propertyDataDtos.Add(BuildDto(currentVersionId, property, propertyValue.LanguageId, propertyValue.Segment, propertyValue.EditedValue)); + propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.EditedValue)); // deal with missing edit value (fix inconsistencies) else if (propertyValue.PublishedValue != null) - propertyDataDtos.Add(BuildDto(currentVersionId, property, propertyValue.LanguageId, propertyValue.Segment, propertyValue.PublishedValue)); + propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.PublishedValue)); // use explicit equals here, else object comparison fails at comparing eg strings var sameValues = propertyValue.PublishedValue == null ? propertyValue.EditedValue == null : propertyValue.PublishedValue.Equals(propertyValue.EditedValue); @@ -123,7 +125,7 @@ namespace Umbraco.Core.Persistence.Factories { // not publishing = only deal with edit values if (propertyValue.EditedValue != null) - propertyDataDtos.Add(BuildDto(currentVersionId, property, propertyValue.LanguageId, propertyValue.Segment, propertyValue.EditedValue)); + propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.EditedValue)); } edited = true; } diff --git a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs index 36dd10c3fb..26e5255d15 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs @@ -7,7 +7,7 @@ namespace Umbraco.Core.Persistence.Repositories ILanguage GetByCultureName(string cultureName); ILanguage GetByIsoCode(string isoCode); - int GetIdByIsoCode(string isoCode); - string GetIsoCodeById(int id); + int? GetIdByIsoCode(string isoCode); + string GetIsoCodeById(int? id); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 84ff426e91..1094d7fb7f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -35,12 +35,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement where TEntity : class, IUmbracoEntity where TRepository : class, IRepository { - protected ContentRepositoryBase(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger) + protected ContentRepositoryBase(IScopeAccessor scopeAccessor, CacheHelper cache, ILanguageRepository languageRepository, ILogger logger) : base(scopeAccessor, cache, logger) - { } + { + LanguageRepository = languageRepository; + } protected abstract TRepository This { get; } + protected ILanguageRepository LanguageRepository { get; } + protected PropertyEditorCollection PropertyEditors => Current.PropertyEditors; // fixme inject #region Versions @@ -464,11 +468,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { propertyDataDtos.AddRange(propertyDataDtos1); if (temp.VersionId == temp.PublishedVersionId) // dirty corner case - propertyDataDtos.AddRange(propertyDataDtos1.Select(x => x.Clone(-1))); + propertyDataDtos.AddRange(propertyDataDtos1.Select(x => x.Clone(-1))); } if (temp.VersionId != temp.PublishedVersionId && indexedPropertyDataDtos.TryGetValue(temp.PublishedVersionId, out var propertyDataDtos2)) propertyDataDtos.AddRange(propertyDataDtos2); - var properties = PropertyFactory.BuildEntities(compositionProperties, propertyDataDtos, temp.PublishedVersionId).ToList(); + var properties = PropertyFactory.BuildEntities(compositionProperties, propertyDataDtos, temp.PublishedVersionId, LanguageRepository).ToList(); // deal with tags foreach (var property in properties) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index cc6247210b..848019cf95 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -25,19 +25,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private readonly IContentTypeRepository _contentTypeRepository; private readonly ITemplateRepository _templateRepository; private readonly ITagRepository _tagRepository; - private readonly ILanguageRepository _languageRepository; private readonly CacheHelper _cacheHelper; private PermissionRepository _permissionRepository; private readonly ContentByGuidReadRepository _contentByGuidReadRepository; private readonly IScopeAccessor _scopeAccessor; public DocumentRepository(IScopeAccessor scopeAccessor, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IContentSection settings) - : base(scopeAccessor, cacheHelper, logger) + : base(scopeAccessor, cacheHelper, languageRepository, logger) { _contentTypeRepository = contentTypeRepository ?? throw new ArgumentNullException(nameof(contentTypeRepository)); _templateRepository = templateRepository ?? throw new ArgumentNullException(nameof(templateRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); - _languageRepository = languageRepository; _cacheHelper = cacheHelper; _scopeAccessor = scopeAccessor; _contentByGuidReadRepository = new ContentByGuidReadRepository(this, scopeAccessor, cacheHelper, logger); @@ -317,7 +315,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, content.PublishedVersionId, entity.Properties, out var edited); + var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, content.PublishedVersionId, entity.Properties, LanguageRepository, out var edited); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -462,7 +460,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var deletePropertyDataSql = Sql().Delete().WhereIn(x => x.VersionId, versionToDelete); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, publishing ? content.PublishedVersionId : 0, entity.Properties, out var edited); + var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, publishing ? content.PublishedVersionId : 0, entity.Properties, LanguageRepository, out var edited); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -904,12 +902,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (variations.TryGetValue(content.VersionId, out var variation)) foreach (var v in variation) { - content.SetName(v.LanguageId, v.Name); + content.SetName(v.Culture, v.Name); } if (content.PublishedVersionId > 0 && variations.TryGetValue(content.PublishedVersionId, out variation)) foreach (var v in variation) { - content.SetPublishName(v.LanguageId, v.Name); + content.SetPublishName(v.Culture, v.Name); } } @@ -940,7 +938,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement variation.Add(new CultureVariation { - LanguageId = dto.LanguageId, + Culture = LanguageRepository.GetIsoCodeById(dto.LanguageId), Name = dto.Name, Available = dto.Available }); @@ -955,7 +953,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement yield return new ContentVersionCultureVariationDto { VersionId = content.VersionId, - LanguageId = culture, + LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), Name = name }; @@ -965,14 +963,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement yield return new ContentVersionCultureVariationDto { VersionId = content.PublishedVersionId, - LanguageId = culture, + LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), Name = name }; } private class CultureVariation { - public int LanguageId { get; set; } + public string Culture { get; set; } public string Name { get; set; } public bool Available { get; set; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index ae9336593d..f97d2707b2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// internal class LanguageRepository : NPocoRepositoryBase, ILanguageRepository { - private readonly Dictionary _codeIdMap = new Dictionary(); + private readonly Dictionary _codeIdMap = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _idCodeMap = new Dictionary(); public LanguageRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger) @@ -62,7 +62,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement foreach (var language in languages) { _codeIdMap[language.IsoCode] = language.Id; - _idCodeMap[language.Id] = language.IsoCode; + _idCodeMap[language.Id] = language.IsoCode.ToLowerInvariant(); } } @@ -211,15 +211,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way var id = GetIdByIsoCode(isoCode, throwOnNotFound: false); - return id > 0 ? Get(id) : null; + return id.HasValue ? Get(id.Value) : null; } // fast way of getting an id for an isoCode - avoiding cloning // _codeIdMap is rebuilt whenever PerformGetAll runs - public int GetIdByIsoCode(string isoCode) => GetIdByIsoCode(isoCode, throwOnNotFound: true); + public int? GetIdByIsoCode(string isoCode) => GetIdByIsoCode(isoCode, throwOnNotFound: true); - private int GetIdByIsoCode(string isoCode, bool throwOnNotFound) + private int? GetIdByIsoCode(string isoCode, bool throwOnNotFound) { + if (isoCode == null) return null; + TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way lock (_codeIdMap) { @@ -232,14 +234,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // fast way of getting an isoCode for an id - avoiding cloning // _idCodeMap is rebuilt whenever PerformGetAll runs - public string GetIsoCodeById(int id) => GetIsoCodeById(id, throwOnNotFound: true); + public string GetIsoCodeById(int? id) => GetIsoCodeById(id, throwOnNotFound: true); - private string GetIsoCodeById(int id, bool throwOnNotFound) + private string GetIsoCodeById(int? id, bool throwOnNotFound) { + if (id == null) return null; + TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way lock (_codeIdMap) // yes, we want to lock _codeIdMap { - if (_idCodeMap.TryGetValue(id, out var isoCode)) return isoCode; + if (_idCodeMap.TryGetValue(id.Value, out var isoCode)) return isoCode; } if (throwOnNotFound) throw new ArgumentException($"Id {id} does not correspond to an existing language.", nameof(id)); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs index a08ecef98d..dfb30a9cb3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs @@ -25,8 +25,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private readonly ITagRepository _tagRepository; private readonly MediaByGuidReadRepository _mediaByGuidReadRepository; - public MediaRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection) - : base(scopeAccessor, cache, logger) + public MediaRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection, ILanguageRepository languageRepository) + : base(scopeAccessor, cache, languageRepository, logger) { _mediaTypeRepository = mediaTypeRepository ?? throw new ArgumentNullException(nameof(mediaTypeRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); @@ -279,7 +279,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(mediaVersionDto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -336,7 +336,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // replace the property data var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == media.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs index 67dbf758db..04e5d64b06 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs @@ -23,8 +23,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private readonly ITagRepository _tagRepository; private readonly IMemberGroupRepository _memberGroupRepository; - public MemberRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository) - : base(scopeAccessor, cache, logger) + public MemberRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, ILanguageRepository languageRepository) + : base(scopeAccessor, cache, languageRepository, logger) { _memberTypeRepository = memberTypeRepository ?? throw new ArgumentNullException(nameof(memberTypeRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); @@ -306,7 +306,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(dto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -371,7 +371,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // replace the property data var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == member.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); diff --git a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs index ea95f8708f..f235a95aa8 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs @@ -272,16 +272,16 @@ namespace Umbraco.Core.PropertyEditors /// /// /// - /// + /// /// /// /// /// The object returned will automatically be serialized into json notation. For most property editors /// the value returned is probably just a string but in some cases a json structure will be returned. /// - public virtual object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null) + public virtual object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) { - var val = property.GetValue(languageId, segment); + var val = property.GetValue(culture, segment); if (val == null) return string.Empty; switch (ValueTypes.ToStorageType(ValueType)) @@ -343,12 +343,8 @@ namespace Umbraco.Core.PropertyEditors continue; var xElement = new XElement(nodeName); - if (pvalue.LanguageId.HasValue) - { - var language = localizationService.GetLanguageById(pvalue.LanguageId.Value); - if (language == null) continue; // uh? - xElement.Add(new XAttribute("lang", language.IsoCode)); - } + if (pvalue.Culture != null) + xElement.Add(new XAttribute("lang", pvalue.Culture)); if (pvalue.Segment != null) xElement.Add(new XAttribute("segment", pvalue.Segment)); diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs index 9e31f94121..b5ed7c5917 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs @@ -61,7 +61,7 @@ namespace Umbraco.Core.PropertyEditors /// /// Converts a property value to a value for the editor. /// - object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null); + object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null); // fixme - editing - document or remove these // why property vs propertyType? services should be injected! etc... diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index cb56b39e2c..abbab0ef39 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -354,7 +354,7 @@ namespace Umbraco.Core.Services /// /// Saves and publishes a document branch. /// - IEnumerable SaveAndPublishBranch(IContent content, bool force, int? languageId = null, string segment = null, int userId = 0); + IEnumerable SaveAndPublishBranch(IContent content, bool force, string culture = null, string segment = null, int userId = 0); /// /// Saves and publishes a document branch. diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs index d61ee5e310..cda28d2818 100644 --- a/src/Umbraco.Core/Services/ILocalizationService.cs +++ b/src/Umbraco.Core/Services/ILocalizationService.cs @@ -120,7 +120,7 @@ namespace Umbraco.Core.Services /// /// Gets a language identifier by its iso code. /// - int GetLanguageIdByIsoCode(string isoCode); + int? GetLanguageIdByIsoCode(string isoCode); /// /// Gets all available languages diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index bfa6c5916d..84e616d4d7 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -1103,14 +1103,14 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable SaveAndPublishBranch(IContent content, bool force, int? languageId = null, string segment = null, int userId = 0) + public IEnumerable SaveAndPublishBranch(IContent content, bool force, string culture = null, string segment = null, int userId = 0) { segment = segment?.ToLowerInvariant(); - bool IsEditing(IContent c, int? l, string s) - => c.Properties.Any(x => x.Values.Where(y => y.LanguageId == l && y.Segment == s).Any(y => y.EditedValue != y.PublishedValue)); + bool IsEditing(IContent c, string l, string s) + => c.Properties.Any(x => x.Values.Where(y => y.Culture == l && y.Segment == s).Any(y => y.EditedValue != y.PublishedValue)); - return SaveAndPublishBranch(content, force, document => IsEditing(document, languageId, segment), document => document.PublishValues(languageId, segment), userId); + return SaveAndPublishBranch(content, force, document => IsEditing(document, culture, segment), document => document.PublishValues(culture, segment), userId); } /// diff --git a/src/Umbraco.Core/Services/Implement/LocalizationService.cs b/src/Umbraco.Core/Services/Implement/LocalizationService.cs index bf01605416..104268f5e8 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizationService.cs @@ -317,7 +317,7 @@ namespace Umbraco.Core.Services.Implement } /// - public int GetLanguageIdByIsoCode(string isoCode) + public int? GetLanguageIdByIsoCode(string isoCode) { using (ScopeProvider.CreateScope(autoComplete: true)) { diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 77b78f355d..7549d4ba2b 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -137,6 +137,7 @@ + diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 8965d60018..6fba071709 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -286,7 +286,7 @@ AnotherContentFinder public void GetDataEditors() { var types = _typeLoader.GetDataEditors(); - Assert.AreEqual(42, types.Count()); + Assert.AreEqual(43, types.Count()); } [Test] diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 3b293621d5..70e3d574b3 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -65,6 +65,8 @@ namespace Umbraco.Tests.Models var propertyType = new PropertyType("editor", ValueStorageType.Nvarchar) { Alias = "prop" }; var prop = new Property(propertyType); + const string langFr = "fr-FR"; + // can set value // and get edited and published value // because non-publishing @@ -84,8 +86,8 @@ namespace Umbraco.Tests.Models Assert.IsNull(prop.GetValue(published: true)); // cannot set non-supported variation value - Assert.Throws(() => prop.SetValue("x", 1)); - Assert.IsNull(prop.GetValue(1)); + Assert.Throws(() => prop.SetValue("x", langFr)); + Assert.IsNull(prop.GetValue(langFr)); // can publish value // and get edited and published values @@ -109,41 +111,41 @@ namespace Umbraco.Tests.Models // can set value // and get values - prop.SetValue("c", 1); + prop.SetValue("c", langFr); Assert.AreEqual("b", prop.GetValue()); Assert.IsNull(prop.GetValue(published: true)); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.IsNull(prop.GetValue(1, published: true)); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.IsNull(prop.GetValue(langFr, published: true)); // can publish value // and get edited and published values - prop.PublishValue(1); + prop.PublishValue(langFr); Assert.AreEqual("b", prop.GetValue()); Assert.IsNull(prop.GetValue(published: true)); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.AreEqual("c", prop.GetValue(1, published: true)); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.AreEqual("c", prop.GetValue(langFr, published: true)); // can clear all prop.ClearPublishedAllValues(); Assert.AreEqual("b", prop.GetValue()); Assert.IsNull(prop.GetValue(published: true)); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.IsNull(prop.GetValue(1, published: true)); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.IsNull(prop.GetValue(langFr, published: true)); // can publish all prop.PublishAllValues(); Assert.AreEqual("b", prop.GetValue()); Assert.AreEqual("b", prop.GetValue(published: true)); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.AreEqual("c", prop.GetValue(1, published: true)); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.AreEqual("c", prop.GetValue(langFr, published: true)); // same for culture - prop.ClearPublishedCultureValues(1); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.IsNull(prop.GetValue(1, published: true)); - prop.PublishCultureValues(1); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.AreEqual("c", prop.GetValue(1, published: true)); + prop.ClearPublishedCultureValues(langFr); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.IsNull(prop.GetValue(langFr, published: true)); + prop.PublishCultureValues(langFr); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.AreEqual("c", prop.GetValue(langFr, published: true)); prop.ClearPublishedCultureValues(); Assert.AreEqual("b", prop.GetValue()); @@ -159,8 +161,8 @@ namespace Umbraco.Tests.Models var contentType = new ContentType(-1) { Alias = "contentType" }; var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; - const int langFr = 1; - const int langUk = 2; + const string langFr = "fr-FR"; + const string langUk = "en-UK"; Assert.Throws(() => content.SetName(langFr, "name-fr")); @@ -188,6 +190,8 @@ namespace Umbraco.Tests.Models [Test] public void ContentTests() { + const string langFr = "fr-FR"; + var propertyType = new PropertyType("editor", ValueStorageType.Nvarchar) { Alias = "prop" }; var contentType = new ContentType(-1) { Alias = "contentType" }; contentType.AddPropertyType(propertyType); @@ -202,8 +206,8 @@ namespace Umbraco.Tests.Models Assert.IsNull(content.GetValue("prop", published: true)); // cannot set non-supported variation value - Assert.Throws(() => content.SetValue("prop", "x", 1)); - Assert.IsNull(content.GetValue("prop", 1)); + Assert.Throws(() => content.SetValue("prop", "x", langFr)); + Assert.IsNull(content.GetValue("prop", langFr)); // can publish value // and get edited and published values @@ -228,41 +232,43 @@ namespace Umbraco.Tests.Models // can set value // and get values - content.SetValue("prop", "c", 1); + content.SetValue("prop", "c", langFr); Assert.AreEqual("b", content.GetValue("prop")); Assert.IsNull(content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.IsNull(content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.IsNull(content.GetValue("prop", langFr, published: true)); // can publish value // and get edited and published values - content.PublishValues(1); + Assert.Throws(() => content.PublishValues(langFr)); // no name + content.SetName(langFr, "name-fr"); + content.PublishValues(langFr); Assert.AreEqual("b", content.GetValue("prop")); Assert.IsNull(content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); // can clear all content.ClearAllPublishedValues(); Assert.AreEqual("b", content.GetValue("prop")); Assert.IsNull(content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.IsNull(content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.IsNull(content.GetValue("prop", langFr, published: true)); // can publish all content.PublishAllValues(); Assert.AreEqual("b", content.GetValue("prop")); Assert.AreEqual("b", content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); // same for culture - content.ClearCulturePublishedValues(1); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.IsNull(content.GetValue("prop", 1, published: true)); - content.PublishCultureValues(1); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + content.ClearCulturePublishedValues(langFr); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.IsNull(content.GetValue("prop", langFr, published: true)); + content.PublishCultureValues(langFr); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); content.ClearCulturePublishedValues(); Assert.AreEqual("b", content.GetValue("prop")); @@ -273,21 +279,21 @@ namespace Umbraco.Tests.Models var other = new Content("other", -1, contentType) { Id = 2, VersionId = 1 }; other.SetValue("prop", "o"); - other.SetValue("prop", "o1", 1); + other.SetValue("prop", "o1", langFr); // can copy other's edited value content.CopyAllValues(other); Assert.AreEqual("o", content.GetValue("prop")); Assert.AreEqual("b", content.GetValue("prop", published: true)); - Assert.AreEqual("o1", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + Assert.AreEqual("o1", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); // can copy self's published value content.CopyAllValues(content); Assert.AreEqual("b", content.GetValue("prop")); Assert.AreEqual("b", content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); } [Test] diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs index 2e72a2e14c..53985b7897 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -36,7 +36,7 @@ namespace Umbraco.Tests.Persistence.Repositories mediaTypeRepository = new MediaTypeRepository(scopeAccessor, cacheHelper, Logger); var tagRepository = new TagRepository(scopeAccessor, cacheHelper, Logger); - var repository = new MediaRepository(scopeAccessor, cacheHelper, Logger, mediaTypeRepository, tagRepository, Mock.Of()); + var repository = new MediaRepository(scopeAccessor, cacheHelper, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs index 4f901935dc..d07d2dda9a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.Persistence.Repositories memberTypeRepository = new MemberTypeRepository(accessor, DisabledCache, Logger); memberGroupRepository = new MemberGroupRepository(accessor, DisabledCache, Logger); var tagRepo = new TagRepository(accessor, DisabledCache, Logger); - var repository = new MemberRepository(accessor, DisabledCache, Logger, memberTypeRepository, memberGroupRepository, tagRepo); + var repository = new MemberRepository(accessor, DisabledCache, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index 216fb08ecd..90230e15c7 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; @@ -964,7 +965,7 @@ namespace Umbraco.Tests.Persistence.Repositories var accessor = (IScopeAccessor) provider; var tagRepository = new TagRepository(accessor, DisabledCache, Logger); mediaTypeRepository = new MediaTypeRepository(accessor, DisabledCache, Logger); - var repository = new MediaRepository(accessor, DisabledCache, Logger, mediaTypeRepository, tagRepository, Mock.Of()); + var repository = new MediaRepository(accessor, DisabledCache, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of()); return repository; } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 56d5bfbc0c..2688629c5c 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Persistence.Repositories var accessor = (IScopeAccessor) provider; mediaTypeRepository = new MediaTypeRepository(accessor, CacheHelper, Mock.Of()); var tagRepository = new TagRepository(accessor, CacheHelper, Mock.Of()); - var repository = new MediaRepository(accessor, CacheHelper, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of()); + var repository = new MediaRepository(accessor, CacheHelper, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index a916a2d51e..22e110dd20 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -242,10 +242,10 @@ namespace Umbraco.Tests.Published _owner = owner; } - public override bool HasValue(int? languageId = null, string segment = null) => _hasValue; - public override object GetSourceValue(int? languageId = null, string segment = null) => _sourceValue; - public override object GetValue(int? languageId = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview); - public override object GetXPathValue(int? languageId = null, string segment = null) => throw new WontImplementException(); + public override bool HasValue(string culture = null, string segment = null) => _hasValue; + public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; + public override object GetValue(string culture = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview); + public override object GetXPathValue(string culture = null, string segment = null) => throw new WontImplementException(); } class TestPublishedContent : PublishedContentBase diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs index 8f6ee5c2b1..7a6c684eb4 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs @@ -256,10 +256,10 @@ namespace Umbraco.Tests.PublishedContent public bool SolidHasValue { get; set; } public object SolidXPathValue { get; set; } - public object GetSourceValue(int? languageId = null, string segment = null) => SolidSourceValue; - public object GetValue(int? languageId = null, string segment = null) => SolidValue; - public object GetXPathValue(int? languageId = null, string segment = null) => SolidXPathValue; - public bool HasValue(int? languageId = null, string segment = null) => SolidHasValue; + 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; } [PublishedModel("ContentType2")] diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 0e726064f4..04ed54d81c 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -356,7 +356,7 @@ namespace Umbraco.Tests.PublishedContent var result = doc.Ancestors().OrderBy(x => x.Level) .Single() .Descendants() - .FirstOrDefault(x => x.Value("selectedNodes", "").Split(',').Contains("1173")); + .FirstOrDefault(x => x.Value("selectedNodes", defaultValue: "").Split(',').Contains("1173")); Assert.IsNotNull(result); } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 79929ff29d..b3bdf4f1ba 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2506,200 +2506,200 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var content = contentService.Create("Home US", - 1, "umbTextpage"); - // act + // act - content.SetValue("author", "Barack Obama"); - content.SetValue("prop", "value-fr1", langFr.Id); - content.SetValue("prop", "value-uk1", langUk.Id); - content.SetName(langFr.Id, "name-fr"); - content.SetName(langUk.Id, "name-uk"); - contentService.Save(content); + content.SetValue("author", "Barack Obama"); + content.SetValue("prop", "value-fr1", langFr.IsoCode); + content.SetValue("prop", "value-uk1", langUk.IsoCode); + content.SetName(langFr.IsoCode, "name-fr"); + content.SetName(langUk.IsoCode, "name-uk"); + contentService.Save(content); - // content has been saved, - // it has names, but no publishNames, and no published cultures + // content has been saved, + // it has names, but no publishNames, and no published cultures - var content2 = contentService.GetById(content.Id); + var content2 = contentService.GetById(content.Id); - Assert.AreEqual("Home US", content2.Name); - Assert.AreEqual("name-fr", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetName(langUk.Id)); + Assert.AreEqual("Home US", content2.Name); + Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id)); - Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true)); - Assert.IsNull(content2.GetValue("prop", langUk.Id, published: true)); + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.IsNull(content2.GetValue("prop", langUk.IsoCode, published: true)); - Assert.IsNull(content2.PublishName); - Assert.IsNull(content2.GetPublishName(langFr.Id)); - Assert.IsNull(content2.GetPublishName(langUk.Id)); + Assert.IsNull(content2.PublishName); + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.IsNull(content2.GetPublishName(langUk.IsoCode)); - Assert.IsTrue(content.IsCultureAvailable(langFr.Id)); - Assert.IsTrue(content.IsCultureAvailable(langUk.Id)); - Assert.IsFalse(content.IsCultureAvailable(langDe.Id)); + Assert.IsTrue(content.IsCultureAvailable(langFr.IsoCode)); + Assert.IsTrue(content.IsCultureAvailable(langUk.IsoCode)); + Assert.IsFalse(content.IsCultureAvailable(langDe.IsoCode)); - Assert.IsFalse(content.IsCulturePublished(langFr.Id)); - Assert.IsFalse(content.IsCulturePublished(langUk.Id)); + Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode)); - // act + // act - content.PublishValues(langFr.Id); - content.PublishValues(langUk.Id); - contentService.SaveAndPublish(content); + content.PublishValues(langFr.IsoCode); + content.PublishValues(langUk.IsoCode); + contentService.SaveAndPublish(content); - // both FR and UK have been published, - // and content has been published, - // it has names, publishNames, and published cultures + // both FR and UK have been published, + // and content has been published, + // it has names, publishNames, and published cultures - content2 = contentService.GetById(content.Id); + content2 = contentService.GetById(content.Id); - Assert.AreEqual("Home US", content2.Name); - Assert.AreEqual("name-fr", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetName(langUk.Id)); + Assert.AreEqual("Home US", content2.Name); + Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode)); - Assert.IsNull(content2.PublishName); // we haven't published InvariantNeutral - Assert.AreEqual("name-fr", content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + Assert.IsNull(content2.PublishName); // we haven't published InvariantNeutral + Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode)); + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); - Assert.IsTrue(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); - // act + // act - content.PublishValues(); - contentService.SaveAndPublish(content); + content.PublishValues(); + contentService.SaveAndPublish(content); - // now it has publish name for invariant neutral + // now it has publish name for invariant neutral - content2 = contentService.GetById(content.Id); + content2 = contentService.GetById(content.Id); - Assert.AreEqual("Home US", content2.PublishName); + Assert.AreEqual("Home US", content2.PublishName); - // act + // act - content.SetName(null, "Home US2"); - content.SetName(langFr.Id, "name-fr2"); - content.SetName(langUk.Id, "name-uk2"); - content.SetValue("author", "Barack Obama2"); - content.SetValue("prop", "value-fr2", langFr.Id); - content.SetValue("prop", "value-uk2", langUk.Id); - contentService.Save(content); + content.SetName(null, "Home US2"); + content.SetName(langFr.IsoCode, "name-fr2"); + content.SetName(langUk.IsoCode, "name-uk2"); + content.SetValue("author", "Barack Obama2"); + content.SetValue("prop", "value-fr2", langFr.IsoCode); + content.SetValue("prop", "value-uk2", langUk.IsoCode); + contentService.Save(content); - // content has been saved, - // it has updated names, unchanged publishNames, and published cultures + // content has been saved, + // it has updated names, unchanged publishNames, and published cultures - content2 = contentService.GetById(content.Id); + content2 = contentService.GetById(content.Id); - Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); - Assert.AreEqual("Home US", content2.PublishName); - Assert.AreEqual("name-fr", content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + Assert.AreEqual("Home US", content2.PublishName); + Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - Assert.AreEqual("Barack Obama2", content2.GetValue("author")); - Assert.AreEqual("Barack Obama", content2.GetValue("author", published: true)); + Assert.AreEqual("Barack Obama2", content2.GetValue("author")); + Assert.AreEqual("Barack Obama", content2.GetValue("author", published: true)); - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); - Assert.IsTrue(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); - // act - // cannot just 'save' since we are changing what's published! + // act + // cannot just 'save' since we are changing what's published! - content.ClearPublishedValues(langFr.Id); - contentService.SaveAndPublish(content); + content.ClearPublishedValues(langFr.IsoCode); + contentService.SaveAndPublish(content); - // content has been published, - // the french culture is gone + // content has been published, + // the french culture is gone - content2 = contentService.GetById(content.Id); + content2 = contentService.GetById(content.Id); - Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); - Assert.AreEqual("Home US", content2.PublishName); - Assert.IsNull(content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + Assert.AreEqual("Home US", content2.PublishName); + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id)); - Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); - Assert.IsFalse(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); - // act + // act - contentService.Unpublish(content); + contentService.Unpublish(content); - // content has been unpublished, - // but properties, names, etc. retain their 'published' values so the content - // can be re-published in its exact original state (before being unpublished) - // - // BEWARE! - // in order for a content to be unpublished as a whole, and then republished in - // its exact previous state, properties and names etc. retain their published - // values even though the content is not published - hence many things being - // non-null or true below - always check against content.Published to be sure + // content has been unpublished, + // but properties, names, etc. retain their 'published' values so the content + // can be re-published in its exact original state (before being unpublished) + // + // BEWARE! + // in order for a content to be unpublished as a whole, and then republished in + // its exact previous state, properties and names etc. retain their published + // values even though the content is not published - hence many things being + // non-null or true below - always check against content.Published to be sure - content2 = contentService.GetById(content.Id); + content2 = contentService.GetById(content.Id); - Assert.IsFalse(content2.Published); + Assert.IsFalse(content2.Published); - Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); - Assert.AreEqual("Home US", content2.PublishName); // not null, see note above - Assert.IsNull(content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); // not null, see note above + Assert.AreEqual("Home US", content2.PublishName); // not null, see note above + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); // not null, see note above - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id)); - Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); // has value, see note above + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); // has value, see note above - Assert.IsFalse(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); // still true, see note above + Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); // still true, see note above - // act + // act - contentService.SaveAndPublish(content); + contentService.SaveAndPublish(content); - // content has been re-published, - // everything is back to what it was before being unpublished + // content has been re-published, + // everything is back to what it was before being unpublished - content2 = contentService.GetById(content.Id); + content2 = contentService.GetById(content.Id); - Assert.IsTrue(content2.Published); + Assert.IsTrue(content2.Published); - Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); - Assert.AreEqual("Home US", content2.PublishName); - Assert.IsNull(content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + Assert.AreEqual("Home US", content2.PublishName); + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id)); - Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); - Assert.IsFalse(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); } private IEnumerable CreateContentHierarchy() diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 47a847ad43..25ac6314e2 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -140,8 +140,8 @@ namespace Umbraco.Tests.TestHelpers { // compare values var actualProperty = (Property) actual; - var expectedPropertyValues = expectedProperty.Values.OrderBy(x => x.LanguageId).ThenBy(x => x.Segment).ToArray(); - var actualPropertyValues = actualProperty.Values.OrderBy(x => x.LanguageId).ThenBy(x => x.Segment).ToArray(); + var expectedPropertyValues = expectedProperty.Values.OrderBy(x => x.Culture).ThenBy(x => x.Segment).ToArray(); + var actualPropertyValues = actualProperty.Values.OrderBy(x => x.Culture).ThenBy(x => x.Segment).ToArray(); if (expectedPropertyValues.Length != actualPropertyValues.Length) Assert.Fail($"{property.DeclaringType.Name}.{property.Name}: Expected {expectedPropertyValues.Length} but got {actualPropertyValues.Length}."); for (var i = 0; i < expectedPropertyValues.Length; i++) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index dce9ca531d..6eb8db3c61 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -265,8 +265,8 @@ namespace Umbraco.Web.Editors HandleContentNotFound(id); return null;//irrelevant since the above throws } - - var content = MapToDisplay(foundContent, languageId); + + var content = MapToDisplay(foundContent, GetLanguageCulture(languageId)); return content; } @@ -606,7 +606,7 @@ namespace Umbraco.Web.Editors { //ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue! // add the modelstate to the outgoing object and throw a validation message - var forDisplay = MapToDisplay(contentItem.PersistedContent, contentItem.LanguageId); + var forDisplay = MapToDisplay(contentItem.PersistedContent, GetLanguageCulture(contentItem.LanguageId)); forDisplay.Errors = ModelState.ToErrorDictionary(); throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay)); @@ -643,14 +643,14 @@ namespace Umbraco.Web.Editors else { //publish the item and check if it worked, if not we will show a diff msg below - contentItem.PersistedContent.PublishValues(contentItem.LanguageId); //we are not checking for a return value here because we've alraedy pre-validated the property values + contentItem.PersistedContent.PublishValues(GetLanguageCulture(contentItem.LanguageId)); //we are not checking for a return value here because we've alraedy pre-validated the property values //check if we are publishing other variants and validate them var allLangs = Services.LocalizationService.GetAllLanguages().ToList(); var variantsToValidate = contentItem.PublishVariations.Where(x => x.LanguageId != contentItem.LanguageId).ToList(); foreach (var publishVariation in variantsToValidate) { - if (!contentItem.PersistedContent.PublishValues(publishVariation.LanguageId)) + if (!contentItem.PersistedContent.PublishValues(GetLanguageCulture(publishVariation.LanguageId))) { var errMsg = Services.TextService.Localize("speechBubbles/contentLangValidationError", new[]{allLangs.First(x => x.Id == publishVariation.LanguageId).CultureName}); ModelState.AddModelError("publish_variant_" + publishVariation.LanguageId + "_", errMsg); @@ -664,7 +664,7 @@ namespace Umbraco.Web.Editors .Where(x => x.Mandatory); foreach (var lang in mandatoryLangs) { - if (contentItem.PersistedContent.Validate(lang.Id).Length > 0) + if (contentItem.PersistedContent.Validate(GetLanguageCulture(lang.Id)).Length > 0) { var errMsg = Services.TextService.Localize("speechBubbles/contentReqLangValidationError", new[]{allLangs.First(x => x.Id == lang.Id).CultureName}); ModelState.AddModelError("publish_variant_" + lang.Id + "_", errMsg); @@ -676,7 +676,7 @@ namespace Umbraco.Web.Editors } //get the updated model - var display = MapToDisplay(contentItem.PersistedContent, contentItem.LanguageId); + var display = MapToDisplay(contentItem.PersistedContent, GetLanguageCulture(contentItem.LanguageId)); //lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403 HandleInvalidModelState(display); @@ -977,8 +977,8 @@ namespace Umbraco.Web.Editors base.MapPropertyValues( contentItem, - (save, property) => property.GetValue(save.LanguageId), //get prop val - (save, property, v) => property.SetValue(v, save.LanguageId)); //set prop val + (save, property) => property.GetValue(GetLanguageCulture(save.LanguageId)), //get prop val + (save, property, v) => property.SetValue(v, GetLanguageCulture(save.LanguageId))); //set prop val } /// @@ -1169,23 +1169,28 @@ namespace Umbraco.Web.Editors /// Used to map an instance to a and ensuring a language is present if required /// /// - /// + /// /// - private ContentItemDisplay MapToDisplay(IContent content, int? languageId = null) + private ContentItemDisplay MapToDisplay(IContent content, string culture = null) { //a languageId must exist in the mapping context if this content item has any property type that can be varied by language //otherwise the property validation will fail since it's expecting to be get/set with a language ID. If a languageId is not explicitly //sent up, then it means that the user is editing the default variant language. - if (!languageId.HasValue && content.HasPropertyTypeVaryingByCulture()) + if (culture == null && content.HasPropertyTypeVaryingByCulture()) { - languageId = Services.LocalizationService.GetDefaultVariantLanguage().Id; + culture = Services.LocalizationService.GetDefaultVariantLanguage().IsoCode; } var display = ContextMapper.Map(content, UmbracoContext, - new Dictionary { { ContextMapper.LanguageKey, languageId } }); + new Dictionary { { ContextMapper.CultureKey, culture } }); return display; } + private string GetLanguageCulture(int? languageId) + { + if (languageId == null) return null; + return Core.Composing.Current.Services.LocalizationService.GetLanguageById(languageId.Value).IsoCode; // fixme optimize! + } } } diff --git a/src/Umbraco.Web/Models/ContentExtensions.cs b/src/Umbraco.Web/Models/ContentExtensions.cs index 4a016a895b..3fe81757a8 100644 --- a/src/Umbraco.Web/Models/ContentExtensions.cs +++ b/src/Umbraco.Web/Models/ContentExtensions.cs @@ -96,6 +96,5 @@ namespace Umbraco.Web.Models var defaultLanguage = localizationService.GetAllLanguages().FirstOrDefault(); return defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode); } - } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs index 5f7cc0e052..10fec19b38 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs @@ -46,9 +46,9 @@ namespace Umbraco.Web.Models.Mapping editor = _propertyEditors[Constants.PropertyEditors.Aliases.NoEdit]; } - var languageId = context.GetLanguageId(); + var culture = context.GetCulture(); - if (!languageId.HasValue && property.PropertyType.Variations == ContentVariation.CultureNeutral) + if (culture == null && property.PropertyType.Variations == ContentVariation.CultureNeutral) { //a language Id needs to be set for a property type that can be varried by language throw new InvalidOperationException($"No languageId found in mapping operation when one is required for the culture neutral property type {property.PropertyType.Alias}"); @@ -74,7 +74,7 @@ namespace Umbraco.Web.Models.Mapping } // if no 'IncludeProperties' were specified or this property is set to be included - we will map the value and return. - result.Value = editor.GetValueEditor().ToEditor(property, DataTypeService, languageId); + result.Value = editor.GetValueEditor().ToEditor(property, DataTypeService, culture); return result; } } diff --git a/src/Umbraco.Web/Models/Mapping/ContextMapper.cs b/src/Umbraco.Web/Models/Mapping/ContextMapper.cs index b46cd219d8..5aa8af7668 100644 --- a/src/Umbraco.Web/Models/Mapping/ContextMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContextMapper.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Models.Mapping internal static class ContextMapper { public const string UmbracoContextKey = "ContextMapper.UmbracoContext"; - public const string LanguageKey = "ContextMapper.LanguageId"; + public const string CultureKey = "ContextMapper.Culture"; public static TDestination Map(TSource obj, UmbracoContext umbracoContext) => Mapper.Map(obj, opt => opt.Items[UmbracoContextKey] = umbracoContext); @@ -77,12 +77,12 @@ namespace Umbraco.Web.Models.Mapping /// /// /// - public static int? GetLanguageId(this ResolutionContext resolutionContext) + public static string GetCulture(this ResolutionContext resolutionContext) { - if (!resolutionContext.Options.Items.TryGetValue(LanguageKey, out var obj)) return null; + if (!resolutionContext.Options.Items.TryGetValue(CultureKey, out var obj)) return null; - if (obj is int i) - return i; + if (obj is string s) + return s; return null; } diff --git a/src/Umbraco.Web/Models/Mapping/VariationResolver.cs b/src/Umbraco.Web/Models/Mapping/VariationResolver.cs index 9d3de07d77..ece6f87a33 100644 --- a/src/Umbraco.Web/Models/Mapping/VariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/VariationResolver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using AutoMapper; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; @@ -29,19 +30,19 @@ namespace Umbraco.Web.Models.Mapping { Language = x, Mandatory = x.Mandatory, - Name = source.GetName(x.Id), - Exists = source.IsCultureAvailable(x.Id), // segments ?? + Name = source.GetName(x.IsoCode), + Exists = source.IsCultureAvailable(x.IsoCode), // segments ?? PublishedState = source.PublishedState.ToString(), //Segment = ?? We'll need to populate this one day when we support segments }).ToList(); - var langId = context.GetLanguageId(); + var culture = context.GetCulture(); //set the current variant being edited to the one found in the context or the default if nothing matches var foundCurrent = false; foreach (var variant in variants) { - if (langId.HasValue && langId.Value == variant.Language.Id) + if (culture.InvariantEquals(variant.Language.IsoCode)) { variant.IsCurrent = true; foundCurrent = true; diff --git a/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs b/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs index 31425fdadd..a03ad4aa00 100644 --- a/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs @@ -18,9 +18,9 @@ namespace Umbraco.Web.PropertyEditors Validators.Add(new DateTimeValidator()); } - public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null) + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture= null, string segment = null) { - var date = property.GetValue(languageId, segment).TryConvertTo(); + var date = property.GetValue(culture, segment).TryConvertTo(); if (date.Success == false || date.Result == null) { return String.Empty; diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs index e969d2e45b..0d5f6efaf5 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs @@ -93,11 +93,11 @@ namespace Umbraco.Web.PropertyEditors //copy each of the property values (variants, segments) to the destination foreach (var propertyValue in property.Values) { - var propVal = property.GetValue(propertyValue.LanguageId, propertyValue.Segment); + var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment); if (propVal == null || !(propVal is string str) || str.IsNullOrWhiteSpace()) continue; var sourcePath = _mediaFileSystem.GetRelativePath(str); var copyPath = _mediaFileSystem.CopyFile(args.Copy, property.PropertyType, sourcePath); - args.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.LanguageId, propertyValue.Segment); + args.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment); isUpdated = true; } } @@ -153,11 +153,11 @@ namespace Umbraco.Web.PropertyEditors foreach (var pvalue in property.Values) { - var svalue = property.GetValue(pvalue.LanguageId, pvalue.Segment) as string; + var svalue = property.GetValue(pvalue.Culture, pvalue.Segment) as string; if (string.IsNullOrWhiteSpace(svalue)) - _mediaFileSystem.UploadAutoFillProperties.Reset(model, autoFillConfig, pvalue.LanguageId, pvalue.Segment); + _mediaFileSystem.UploadAutoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment); else - _mediaFileSystem.UploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(svalue), pvalue.LanguageId, pvalue.Segment); + _mediaFileSystem.UploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(svalue), pvalue.Culture, pvalue.Segment); } } } diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs index 6d3c5b4f61..6183641aaf 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs @@ -148,13 +148,13 @@ namespace Umbraco.Web.PropertyEditors //copy each of the property values (variants, segments) to the destination by using the edited value foreach (var propertyValue in property.Values) { - var propVal = property.GetValue(propertyValue.LanguageId, propertyValue.Segment); + var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment); var src = GetFileSrcFromPropertyValue(propVal, out var jo); if (src == null) continue; var sourcePath = _mediaFileSystem.GetRelativePath(src); var copyPath = _mediaFileSystem.CopyFile(args.Copy, property.PropertyType, sourcePath); jo["src"] = _mediaFileSystem.GetUrl(copyPath); - args.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.LanguageId, propertyValue.Segment); + args.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.Culture, propertyValue.Segment); isUpdated = true; } } @@ -209,10 +209,10 @@ namespace Umbraco.Web.PropertyEditors foreach (var pvalue in property.Values) { - var svalue = property.GetValue(pvalue.LanguageId, pvalue.Segment) as string; + var svalue = property.GetValue(pvalue.Culture, pvalue.Segment) as string; if (string.IsNullOrWhiteSpace(svalue)) { - _autoFillProperties.Reset(model, autoFillConfig, pvalue.LanguageId, pvalue.Segment); + _autoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment); continue; } diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs index bce989fb18..98e8346441 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -32,9 +32,9 @@ namespace Umbraco.Web.PropertyEditors /// This is called to merge in the prevalue crops with the value that is saved - similar to the property value converter for the front-end /// - public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null) + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) { - var val = property.GetValue(languageId, segment); + var val = property.GetValue(culture, segment); if (val == null) return null; ImageCropperValue value; diff --git a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs index a6373bd87d..645296a354 100644 --- a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs @@ -85,9 +85,9 @@ namespace Umbraco.Web.PropertyEditors /// /// The legacy property editor saved this data as new line delimited! strange but we have to maintain that. /// - public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null) + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) { - var val = property.GetValue(languageId, segment); + var val = property.GetValue(culture, segment); return val?.ToString().Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries) .Select(x => JObject.FromObject(new {value = x})) ?? new JObject[] { }; diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs index 52933e5846..4cb8fde97a 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs @@ -139,9 +139,9 @@ namespace Umbraco.Web.PropertyEditors // note: there is NO variant support here - public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null) + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) { - var val = property.GetValue(languageId, segment); + var val = property.GetValue(culture, segment); if (val == null || string.IsNullOrWhiteSpace(val.ToString())) return string.Empty; diff --git a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs index 6cd9675f67..25a1c25284 100644 --- a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs @@ -70,12 +70,12 @@ namespace Umbraco.Web.PropertyEditors /// /// /// - /// + /// /// /// - public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null) + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) { - var delimited = base.ToEditor(property, dataTypeService, languageId, segment).ToString(); + var delimited = base.ToEditor(property, dataTypeService, culture, segment).ToString(); return delimited.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); } diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 181471b315..797d0eaf59 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -61,12 +61,12 @@ namespace Umbraco.Web.PropertyEditors /// /// /// - /// + /// /// /// - public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null) + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) { - var val = property.GetValue(languageId, segment); + var val = property.GetValue(culture, segment); if (val == null) return null; diff --git a/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs index f860a5abe7..754cef1f31 100644 --- a/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs @@ -20,15 +20,15 @@ namespace Umbraco.Web.PropertyEditors /// /// /// - /// + /// /// /// /// /// The object returned will always be a string and if the database type is not a valid string type an exception is thrown /// - public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null) + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) { - var val = property.GetValue(languageId, segment); + var val = property.GetValue(culture, segment); if (val == null) return string.Empty; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs index 6fd731cbee..30bb553ae2 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs @@ -159,7 +159,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource pdatas.Add(pdata); var type = PrimitiveSerializer.Char.ReadFrom(stream); - pdata.LanguageId = (int?) ReadObject(type, stream); + pdata.Culture = (string) ReadObject(type, stream); type = PrimitiveSerializer.Char.ReadFrom(stream); pdata.Segment = (string) ReadObject(type, stream); type = PrimitiveSerializer.Char.ReadFrom(stream); @@ -211,7 +211,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource // write each value foreach (var pdata in kvp.Value) { - WriteObject(pdata.LanguageId, stream); + WriteObject(pdata.Culture, stream); WriteObject(pdata.Segment, stream); WriteObject(pdata.Value, stream); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs index ab9d3cdcac..ddb9607575 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs @@ -4,8 +4,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { internal class PropertyData { - [JsonProperty("lang")] - public int? LanguageId { get; set; } + [JsonProperty("culture")] + public string Culture { get; set; } [JsonProperty("seg")] public string Segment { get; set; } @@ -13,4 +13,4 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource [JsonProperty("val")] public object Value { get; set; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 9a74b66897..7fe898b080 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -28,7 +28,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private object _interValue; // the variant source and inter values - private Dictionary _sourceValues; + private Dictionary _sourceValues; // the variant and non-variant object values private CacheValues _cacheValues; @@ -49,16 +49,16 @@ namespace Umbraco.Web.PublishedCache.NuCache { foreach (var sourceValue in sourceValues) { - if (sourceValue.LanguageId == null && sourceValue.Segment == null) + if (sourceValue.Culture == null && sourceValue.Segment == null) { _sourceValue = sourceValue.Value; } else { if (_sourceValues == null) - _sourceValues = new Dictionary(); - _sourceValues[new CompositeIntStringKey(sourceValue.LanguageId, sourceValue.Segment)] - = new SourceInterValue { LanguageId = sourceValue.LanguageId, Segment = sourceValue.Segment, SourceValue = sourceValue.Value }; + _sourceValues = new Dictionary(); + _sourceValues[new CompositeStringStringKey(sourceValue.Culture, sourceValue.Segment)] + = new SourceInterValue { Culture = sourceValue.Culture, Segment = sourceValue.Segment, SourceValue = sourceValue.Value }; } } } @@ -84,7 +84,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; } - public override bool HasValue(int? languageId = null, string segment = null) => _sourceValue != null + public override bool HasValue(string culture = null, string segment = null) => _sourceValue != null && (!(_sourceValue is string) || string.IsNullOrWhiteSpace((string) _sourceValue) == false); // used to cache the recursive *property* for this property @@ -143,9 +143,9 @@ namespace Umbraco.Web.PublishedCache.NuCache } // this is always invoked from within a lock, so does not require its own lock - private object GetInterValue(int? languageId, string segment) + private object GetInterValue(string culture, string segment) { - if (languageId == null && segment == null) + if (culture == null && segment == null) { if (_interInitialized) return _interValue; _interValue = PropertyType.ConvertSourceToInter(_content, _sourceValue, _isPreviewing); @@ -154,11 +154,11 @@ namespace Umbraco.Web.PublishedCache.NuCache } if (_sourceValues == null) - _sourceValues = new Dictionary(); + _sourceValues = new Dictionary(); - var k = new CompositeIntStringKey(languageId, segment); + var k = new CompositeStringStringKey(culture, segment); if (!_sourceValues.TryGetValue(k, out var vvalue)) - _sourceValues[k] = vvalue = new SourceInterValue { LanguageId = languageId, Segment = segment }; + _sourceValues[k] = vvalue = new SourceInterValue { Culture = culture, Segment = segment }; if (vvalue.InterInitialized) return vvalue.InterValue; vvalue.InterValue = PropertyType.ConvertSourceToInter(_content, vvalue.SourceValue, _isPreviewing); @@ -166,45 +166,45 @@ namespace Umbraco.Web.PublishedCache.NuCache return vvalue.InterValue; } - public override object GetSourceValue(int? languageId = null, string segment = null) + public override object GetSourceValue(string culture = null, string segment = null) { - if (languageId == null && segment == null) + if (culture == null && segment == null) return _sourceValue; lock (_locko) { if (_sourceValues == null) return null; - return _sourceValues.TryGetValue(new CompositeIntStringKey(languageId, segment), out var sourceValue) ? sourceValue.SourceValue : null; + return _sourceValues.TryGetValue(new CompositeStringStringKey(culture, segment), out var sourceValue) ? sourceValue.SourceValue : null; } } - public override object GetValue(int? languageId = null, string segment = null) + public override object GetValue(string culture = null, string segment = null) { lock (_locko) { - var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(languageId, segment); + var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment); // initial reference cache level always is .Content const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element; if (cacheValues.ObjectInitialized) return cacheValues.ObjectValue; - cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(languageId, segment), _isPreviewing); + cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing); cacheValues.ObjectInitialized = true; return cacheValues.ObjectValue; } } - public override object GetXPathValue(int? languageId = null, string segment = null) + public override object GetXPathValue(string culture = null, string segment = null) { lock (_locko) { - var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(languageId, segment); + var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment); // initial reference cache level always is .Content const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element; if (cacheValues.XPathInitialized) return cacheValues.XPathValue; - cacheValues.XPathValue = PropertyType.ConvertInterToXPath(_content, initialCacheLevel, GetInterValue(languageId, segment), _isPreviewing); + cacheValues.XPathValue = PropertyType.ConvertInterToXPath(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing); cacheValues.XPathInitialized = true; return cacheValues.XPathValue; } @@ -222,18 +222,18 @@ namespace Umbraco.Web.PublishedCache.NuCache private class CacheValues : CacheValue { - private Dictionary _values; + private Dictionary _values; // this is always invoked from within a lock, so does not require its own lock - public CacheValue For(int? languageId, string segment) + public CacheValue For(string culture, string segment) { - if (languageId == null && segment == null) + if (culture == null && segment == null) return this; if (_values == null) - _values = new Dictionary(); + _values = new Dictionary(); - var k = new CompositeIntStringKey(languageId, segment); + var k = new CompositeStringStringKey(culture, segment); if (!_values.TryGetValue(k, out var value)) _values[k] = value = new CacheValue(); @@ -243,9 +243,14 @@ namespace Umbraco.Web.PublishedCache.NuCache private class SourceInterValue { + private string _culture; private string _segment; - public int? LanguageId { get; set; } + public string Culture + { + get => _culture; + internal set => _culture = value?.ToLowerInvariant(); + } public string Segment { get => _segment; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 992ac62733..7f26f3c753 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -1183,7 +1183,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { var value = published ? pvalue.PublishedValue : pvalue.EditedValue; if (value != null) - pdatas.Add(new PropertyData { LanguageId = pvalue.LanguageId, Segment = pvalue.Segment, Value = value }); + pdatas.Add(new PropertyData { Culture = pvalue.Culture, Segment = pvalue.Segment, Value = value }); //Core.Composing.Current.Logger.Debug($"{content.Id} {prop.Alias} [{pvalue.LanguageId},{pvalue.Segment}] {value} {(published?"pub":"edit")}"); diff --git a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs index 2c5b93f219..d8db937ca8 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.PublishedCache IsMember = propertyType.ContentType.ItemType == PublishedItemType.Member; } - public override bool HasValue(int? languageId = null, string segment = null) + public override bool HasValue(string culture = null, string segment = null) => _sourceValue != null && (!(_sourceValue is string s) || !string.IsNullOrWhiteSpace(s)); // used to cache the CacheValues of this property @@ -136,9 +136,9 @@ namespace Umbraco.Web.PublishedCache return _interValue; } - public override object GetSourceValue(int? languageId = null, string segment = null) => _sourceValue; + public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; - public override object GetValue(int? languageId = null, string segment = null) + public override object GetValue(string culture = null, string segment = null) { GetCacheLevels(out var cacheLevel, out var referenceCacheLevel); @@ -152,7 +152,7 @@ namespace Umbraco.Web.PublishedCache } } - public override object GetXPathValue(int? languageId = null, string segment = null) + public override object GetXPathValue(string culture = null, string segment = null) { GetCacheLevels(out var cacheLevel, out var referenceCacheLevel); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs index 632698c37e..ea8ab925c6 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs @@ -27,13 +27,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// /// Gets the raw value of the property. /// - public override object GetSourceValue(int? languageId = null, string segment = null) => _sourceValue; + public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; // in the Xml cache, everything is a string, and to have a value // you want to have a non-null, non-empty string. - public override bool HasValue(int? languageId = null, string segment = null) => _sourceValue.Trim().Length > 0; + public override bool HasValue(string culture = null, string segment = null) => _sourceValue.Trim().Length > 0; - public override object GetValue(int? languageId = null, string segment = null) + public override object GetValue(string culture = null, string segment = null) { // NOT caching the source (intermediate) value since we'll never need it // everything in Xml cache is per-request anyways @@ -48,7 +48,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return _objectValue; } - public override object GetXPathValue(int? languageId = null, string segment = null) { throw new NotImplementedException(); } + public override object GetXPathValue(string culture = null, string segment = null) { throw new NotImplementedException(); } public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, XmlNode propertyXmlData) : this(propertyType, content, isPreviewing) diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs index 3314bc09fc..6d397ffaa3 100644 --- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedContentPropertyExtension.cs @@ -10,24 +10,24 @@ namespace Umbraco.Web { #region Value - public static T Value(this IPublishedProperty property, int? languageId = null, string segment = null) + public static T Value(this IPublishedProperty property, string culture = null, string segment = null) { - return property.Value(false, default(T), languageId, segment); + return property.Value(false, default(T), culture, segment); } - public static T Value(this IPublishedProperty property, T defaultValue, int? languageId = null, string segment = null) + public static T Value(this IPublishedProperty property, T defaultValue, string culture = null, string segment = null) { - return property.Value(true, defaultValue, languageId, segment); + return property.Value(true, defaultValue, culture, segment); } - internal static T Value(this IPublishedProperty property, bool withDefaultValue, T defaultValue, int? languageId = null, string segment = null) + internal static T Value(this IPublishedProperty property, bool withDefaultValue, T defaultValue, string culture = null, string segment = null) { - if (property.HasValue(languageId, segment) == false && withDefaultValue) return defaultValue; + if (property.HasValue(culture, segment) == false && withDefaultValue) return defaultValue; // else we use .Value so we give the converter a chance to handle the default value differently // eg for IEnumerable it may return Enumerable.Empty instead of null - var value = property.GetValue(languageId, segment); + var value = property.GetValue(culture, segment); // 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 diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index c2a818a926..bde74c0ab1 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -48,10 +48,10 @@ namespace Umbraco.Web /// Gets a value indicating whether the content has a value for a property identified by its alias. /// /// Returns true if GetProperty(alias) is not null and GetProperty(alias).HasValue is true. - public static bool HasValue(this IPublishedElement content, string alias, int? languageId = null, string segment = null) + public static bool HasValue(this IPublishedElement content, string alias, string culture = null, string segment = null) { var prop = content.GetProperty(alias); - return prop != null && prop.HasValue(languageId, segment); + return prop != null && prop.HasValue(culture, segment); } /// @@ -63,8 +63,7 @@ namespace Umbraco.Web /// The value to return if the content has no value for the property. /// Either or depending on whether the content /// has a value for the property identified by the alias. - public static IHtmlString HasValue(this IPublishedElement content, string alias, - string valueIfTrue, string valueIfFalse = null) + public static IHtmlString IfHasValue(this IPublishedElement content, string alias, string valueIfTrue, string valueIfFalse = null) { return content.HasValue(alias) ? new HtmlString(valueIfTrue) @@ -80,7 +79,7 @@ namespace Umbraco.Web /// /// The content. /// The property alias. - /// The variation language. + /// The variation language. /// The variation segment. /// The value of the content's property identified by the alias. /// @@ -89,10 +88,31 @@ 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, int? languageId = null, string segment = null) + public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null) { var property = content.GetProperty(alias); - return property?.GetValue(languageId, segment); + return property?.GetValue(culture, segment); + } + + /// + /// 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 default value. + /// The variation language. + /// The variation segment. + /// 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. + /// 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 IPublishedElement content, string alias, string defaultValue, string culture = null, string segment = null) // fixme - kill + { + var property = content.GetProperty(alias); + return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(culture, segment); } /// @@ -110,31 +130,10 @@ 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 defaultValue, int? languageId = null, string segment = null) // fixme - kill + public static object Value(this IPublishedElement content, string alias, object defaultValue, string culture = null, string segment = null) { var property = content.GetProperty(alias); - return property == null || property.HasValue(languageId, segment) == false ? defaultValue : property.GetValue(languageId, segment); - } - - /// - /// 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 default value. - /// The variation language. - /// The variation segment. - /// 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. - /// 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 IPublishedElement content, string alias, object defaultValue, int? languageId = null, string segment = null) - { - var property = content.GetProperty(alias); - return property == null || property.HasValue(languageId, segment) == false ? defaultValue : property.GetValue(languageId, segment); + return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(culture, segment); } #endregion @@ -147,7 +146,7 @@ namespace Umbraco.Web /// The target property type. /// The content. /// The property alias. - /// The variation language. + /// The variation language. /// The variation segment. /// The value of the content's property identified by the alias, converted to the specified type. /// @@ -156,9 +155,9 @@ 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, int? languageId = null, string segment = null) + public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null) { - return content.Value(alias, false, default(T), languageId, segment); + return content.Value(alias, false, default(T), culture, segment); } /// @@ -168,7 +167,7 @@ namespace Umbraco.Web /// The content. /// The property alias. /// The default value. - /// The variation language. + /// The variation language. /// The variation segment. /// The value of the content's property identified by the alias, converted to the specified type, if it exists, otherwise a default value. /// @@ -177,17 +176,17 @@ 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, T defaultValue, int? languageId = null, string segment = null) + public static T Value(this IPublishedElement content, string alias, T defaultValue, string culture = null, string segment = null) { - return content.Value(alias, true, defaultValue, languageId, segment); + return content.Value(alias, true, defaultValue, culture, segment); } - internal static T Value(this IPublishedElement content, string alias, bool withDefaultValue, T defaultValue, int? languageId = null, string segment = null) // fixme uh? + internal static T Value(this IPublishedElement content, string alias, bool withDefaultValue, T defaultValue, string culture = null, string segment = null) // fixme uh? { var property = content.GetProperty(alias); if (property == null) return defaultValue; - return property.Value(withDefaultValue, defaultValue, languageId, segment); + return property.Value(withDefaultValue, defaultValue, culture, segment); } #endregion diff --git a/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs b/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs index 0a399c24c5..525b1fcf2c 100644 --- a/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs +++ b/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs @@ -34,7 +34,7 @@ namespace Umbraco.Web.WebApi.Binders { return ContextMapper.Map>(content, new Dictionary { - [ContextMapper.LanguageKey] = languageId + [ContextMapper.CultureKey] = languageId }); } } diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index ad1ac877be..e940d01470 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -374,17 +374,17 @@ namespace umbraco _content = content; } - public override bool HasValue(int? languageId = null, string segment = null) + public override bool HasValue(string culture = null, string segment = null) { return _sourceValue != null && ((_sourceValue is string) == false || string.IsNullOrWhiteSpace((string)_sourceValue) == false); } - public override object GetSourceValue(int? languageId = null, string segment = null) + public override object GetSourceValue(string culture = null, string segment = null) { return _sourceValue; } - public override object GetValue(int? languageId = null, string segment = null) + public override object GetValue(string culture = null, string segment = null) { // isPreviewing is true here since we want to preview anyway... const bool isPreviewing = true; @@ -392,7 +392,7 @@ namespace umbraco return PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Unknown, source, isPreviewing); } - public override object GetXPathValue(int? languageId = null, string segment = null) + public override object GetXPathValue(string culture = null, string segment = null) { throw new NotImplementedException(); } From 6b9adfa929e76abb34172d6c954241ebb55077ae Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 21 Apr 2018 11:11:31 +0200 Subject: [PATCH 20/97] Move IsCultureAvailable to IContentBase --- src/Umbraco.Core/Models/Content.cs | 4 -- src/Umbraco.Core/Models/ContentBase.cs | 54 ++++++++++++++----------- src/Umbraco.Core/Models/IContent.cs | 9 ----- src/Umbraco.Core/Models/IContentBase.cs | 11 ++++- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 1f106124b4..bb2192f187 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -253,10 +253,6 @@ namespace Umbraco.Core.Models PublishName = null; _publishNames = null; } - - /// - public bool IsCultureAvailable(string culture) - => !string.IsNullOrWhiteSpace(GetName(culture)); /// public bool IsCulturePublished(string culture) diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 32a26f9dc0..ca5f9024a7 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -121,6 +121,22 @@ namespace Umbraco.Core.Models } } + /// + /// Gets the enumeration of property groups for the entity. + /// fixme is a proxy, kill this + /// + [IgnoreDataMember] + public IEnumerable PropertyGroups => ContentTypeBase.CompositionPropertyGroups; + + /// + /// Gets the numeration of property types for the entity. + /// fixme is a proxy, kill this + /// + [IgnoreDataMember] + public IEnumerable PropertyTypes => ContentTypeBase.CompositionPropertyTypes; + + #region Cultures + /// [DataMember] public virtual IReadOnlyDictionary Names @@ -140,7 +156,7 @@ namespace Umbraco.Core.Models { ClearName(culture); return; - } + } if (culture == null) { @@ -158,6 +174,18 @@ namespace Umbraco.Core.Models OnPropertyChanged(Ps.Value.NamesSelector); } + /// + public virtual string GetName(string culture) + { + if (culture == null) return Name; + if (_names == null) return null; + return _names.TryGetValue(culture, out var name) ? name : null; + } + + /// + public bool IsCultureAvailable(string culture) + => !string.IsNullOrWhiteSpace(GetName(culture)); + private void ClearName(string culture) { if (culture == null) @@ -177,28 +205,8 @@ namespace Umbraco.Core.Models _names = null; OnPropertyChanged(Ps.Value.NamesSelector); } - - /// - public virtual string GetName(string culture) - { - if (culture == null) return Name; - if (_names == null) return null; - return _names.TryGetValue(culture, out var name) ? name : null; - } - - /// - /// Gets the enumeration of property groups for the entity. - /// fixme is a proxy, kill this - /// - [IgnoreDataMember] - public IEnumerable PropertyGroups => ContentTypeBase.CompositionPropertyGroups; - - /// - /// Gets the numeration of property types for the entity. - /// fixme is a proxy, kill this - /// - [IgnoreDataMember] - public IEnumerable PropertyTypes => ContentTypeBase.CompositionPropertyTypes; + + #endregion #region Has, Get, Set, Publish Property Value diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 3df81f7314..69e01f4963 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -80,15 +80,6 @@ namespace Umbraco.Core.Models /// ContentStatus Status { get; } - /// - /// Gets a value indicating whether a given culture is available. - /// - /// - /// A culture becomes available whenever the content name for this culture is - /// non-null, and it becomes unavailable whenever the content name is null. - /// - bool IsCultureAvailable(string culture); - /// /// Gets a value indicating whether a given culture is published. /// diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index f8bb0e6985..93a6e82ada 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -51,9 +51,18 @@ namespace Umbraco.Core.Models /// /// Because a dictionary key cannot be null this cannot get nor set the invariant /// name, which must be get or set via the property. - /// + /// IReadOnlyDictionary Names { get; set; } + /// + /// Gets a value indicating whether a given culture is available. + /// + /// + /// A culture becomes available whenever the content name for this culture is + /// non-null, and it becomes unavailable whenever the content name is null. + /// + bool IsCultureAvailable(string culture); + /// /// List of properties, which make up all the data available for this Content object /// From 48641166b944f046f754b03961d7370774b1f1c4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 01:31:01 +1000 Subject: [PATCH 21/97] WIP - gets 'inbound' routing working to generate culture specific URLs --- .../PublishedContent/IPublishedContent.cs | 10 +-- .../PublishedContentWrapped.cs | 4 +- .../PublishedContent/PublishedCultureName.cs | 19 ++++++ .../Persistence/Dtos/ContentNuDto.cs | 6 ++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Published/NestedContentTests.cs | 1 + .../Published/PublishedSnapshotTestObjects.cs | 1 + .../PublishedContentDataTableTests.cs | 3 +- .../PublishedContentTestElements.cs | 7 +- .../TestControllerActivatorBase.cs | 2 +- .../Testing/TestingTests/MockTests.cs | 2 +- .../Web/TemplateUtilitiesTests.cs | 2 +- .../Models/PublishedContentBase.cs | 4 +- .../ContentPickerValueConverter.cs | 1 + .../MediaPickerValueConverter.cs | 2 + .../MultiNodeTreePickerValueConverter.cs | 2 + .../PublishedCache/IPublishedContentCache.cs | 4 +- .../PublishedCache/NuCache/CacheKeys.cs | 10 ++- .../PublishedCache/NuCache/ContentCache.cs | 34 +++++++--- .../NuCache/DataSource/BTree.cs | 68 ++++++++++++++++++- .../NuCache/DataSource/ContentData.cs | 6 ++ .../DataSource/ContentSerializedData.cs | 17 +++++ .../NuCache/DataSource/CultureVariation.cs | 15 ++++ .../NuCache/DataSource/Database.cs | 19 ++++-- .../NuCache/PublishedContent.cs | 37 +++++++--- .../NuCache/PublishedSnapshotService.cs | 42 +++++++++--- .../PublishedCache/PublishedMember.cs | 2 + .../PublishedContentCache.cs | 6 +- .../XmlPublishedCache/PublishedMediaCache.cs | 2 + .../XmlPublishedCache/XmlPublishedContent.cs | 4 +- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 62 ++++++++++------- .../Routing/CustomRouteUrlProvider.cs | 2 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 4 +- src/Umbraco.Web/Routing/IUrlProvider.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 + src/Umbraco.Web/umbraco.presentation/page.cs | 30 +++++++- 36 files changed, 350 insertions(+), 85 deletions(-) create mode 100644 src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSerializedData.cs create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index f114d94116..f75e59bee0 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -3,17 +3,11 @@ using System.Collections.Generic; namespace Umbraco.Core.Models.PublishedContent { + /// /// /// Represents a cached content. /// - /// - /// SD: A replacement for INode which needs to occur since INode doesn't contain the document type alias - /// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable). - /// Stephan: initially, that was for cached published content only. Now, we're using it also for - /// cached preview (so, maybe unpublished) content. A better name would therefore be ICachedContent, as - /// has been suggested. However, can't change now. Maybe in v7? - /// public interface IPublishedContent : IPublishedElement { #region Content @@ -38,6 +32,8 @@ namespace Umbraco.Core.Models.PublishedContent int Level { get; } string Url { get; } + IReadOnlyDictionary CultureNames { get; } + /// /// Gets a value indicating whether the content is a content (aka a document) or a media. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index a30726012e..528a545f8f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -54,7 +54,9 @@ namespace Umbraco.Core.Models.PublishedContent public virtual int SortOrder => _content.SortOrder; - public virtual string Name => _content.Name; + public virtual string Name => _content.Name; + + public virtual IReadOnlyDictionary CultureNames => _content.CultureNames; public virtual string UrlName => _content.UrlName; diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs new file mode 100644 index 0000000000..59ac875aa4 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs @@ -0,0 +1,19 @@ +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Contains the culture specific data for a item + /// + public struct PublishedCultureName + { + public PublishedCultureName(string name, string urlName) : this() + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + UrlName = urlName ?? throw new ArgumentNullException(nameof(urlName)); + } + + public string Name { get; } + public string UrlName { get; } + } +} diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentNuDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentNuDto.cs index f427c5aa4a..eea3d5d537 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentNuDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentNuDto.cs @@ -16,6 +16,12 @@ namespace Umbraco.Core.Persistence.Dtos [Column("published")] public bool Published { get; set; } + /// + /// Stores serialized JSON representing the content item's property and culture name values + /// + /// + /// Pretty much anything that would require a 1:M lookup is serialized here + /// [Column("data")] [SpecialDbType(SpecialDbTypes.NTEXT)] public string Data { get; set; } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 77b78f355d..c8f4ed577d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -365,6 +365,7 @@ + diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index a916a2d51e..b097358fc3 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -273,6 +273,7 @@ namespace Umbraco.Tests.Published public override int TemplateId { get; } public override int SortOrder { get; } public override string Name { get; } + public override IReadOnlyDictionary CultureNames => throw new NotSupportedException(); public override string UrlName { get; } public override string DocumentTypeAlias { get; } public override int DocumentTypeId { get; } diff --git a/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs b/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs index 48495b2ae7..acfc12d408 100644 --- a/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs +++ b/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs @@ -66,6 +66,7 @@ namespace Umbraco.Tests.Published public int TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } + public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); public string UrlName { get; set; } public string DocumentTypeAlias => ContentType.Alias; public int DocumentTypeId { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 6e7bae28a6..4668a86c78 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -200,7 +200,8 @@ namespace Umbraco.Tests.PublishedContent public Guid Key { get; set; } public int TemplateId { get; set; } public int SortOrder { get; set; } - public string Name { get; set; } + public string Name { get; set; } + public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); public string UrlName { get; set; } public string DocumentTypeAlias { get; set; } public int DocumentTypeId { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs index 8f6ee5c2b1..9241686188 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs @@ -68,12 +68,12 @@ namespace Umbraco.Tests.PublishedContent throw new NotImplementedException(); } - public string GetRouteById(bool preview, int contentId) + public string GetRouteById(bool preview, int contentId, string language = null) { throw new NotImplementedException(); } - public string GetRouteById(int contentId) + public string GetRouteById(int contentId, string language = null) { throw new NotImplementedException(); } @@ -176,7 +176,8 @@ namespace Umbraco.Tests.PublishedContent public Guid Key { get; set; } public int TemplateId { get; set; } public int SortOrder { get; set; } - public string Name { get; set; } + public string Name { get; set; } + public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); public string UrlName { get; set; } public string DocumentTypeAlias { get; private set; } public int DocumentTypeId { get; private set; } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 0b3e5d6efd..7a8188cd0b 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -151,7 +151,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting true); //replace it var urlHelper = new Mock(); - urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var membershipHelper = new MembershipHelper(umbCtx, Mock.Of(), Mock.Of()); diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index b6a5d982c4..580defd5ab 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -73,7 +73,7 @@ namespace Umbraco.Tests.Testing.TestingTests var umbracoContext = TestObjects.GetUmbracoContextMock(); var urlProviderMock = new Mock(); - urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var urlProvider = urlProviderMock.Object; diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 8b52ce1893..e6a5dd8438 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -72,7 +72,7 @@ namespace Umbraco.Tests.Web //setup a mock url provider which we'll use fo rtesting var testUrlProvider = new Mock(); - testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns((UmbracoContext umbCtx, int id, Uri url, UrlProviderMode mode) => { return "/my-test-url"; diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 89a735688c..a89cab2c65 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -93,7 +93,9 @@ namespace Umbraco.Web.Models public abstract Guid Key { get; } public abstract int TemplateId { get; } public abstract int SortOrder { get; } - public abstract string Name { get; } + public abstract string Name { get; } + //TODO: On the base ContentData instance this dictionary contains a CultureVariation, should we expose that model here or a different model? + public abstract IReadOnlyDictionary CultureNames { get; } public abstract string UrlName { get; } public abstract string DocumentTypeAlias { get; } public abstract int DocumentTypeId { get; } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs index ce3fef228f..b9cac88ecf 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs @@ -75,6 +75,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public override object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { + if (inter == null) return null; return inter.ToString(); } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs index 9e9e61cb05..f2873e9466 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs @@ -42,6 +42,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) { + if (source == null) return null; + var nodeIds = source.ToString() .Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) .Select(Udi.Parse) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs index 42fc63011b..50ebe35d11 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs @@ -46,6 +46,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) { + if (source == null) return null; + if (propertyType.EditorAlias.Equals(Constants.PropertyEditors.Aliases.MultiNodeTreePicker)) { var nodeIds = source.ToString() diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs index e033647fdf..64fa5efde4 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs @@ -39,7 +39,7 @@ namespace Umbraco.Web.PublishedCache /// The content unique identifier. /// The route. /// The value of overrides defaults. - string GetRouteById(bool preview, int contentId); + string GetRouteById(bool preview, int contentId, string language = null); /// /// Gets the route for a content identified by its unique identifier. @@ -47,6 +47,6 @@ namespace Umbraco.Web.PublishedCache /// The content unique identifier. /// The route. /// Considers published or unpublished content depending on defaults. - string GetRouteById(int contentId); + string GetRouteById(int contentId, string language = null); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs index a3168732e4..6d21fedb6d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs @@ -11,6 +11,12 @@ namespace Umbraco.Web.PublishedCache.NuCache return previewing ? "D:" : "P:"; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static string LangId(string language) + { + return language != null ? ("-L:" + language) : string.Empty; + } + public static string PublishedContentChildren(Guid contentUid, bool previewing) { return "NuCache.Content.Children[" + DraftOrPub(previewing) + ":" + contentUid + "]"; @@ -50,9 +56,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // a valid ID in the database at that point, whereas content and properties // may be virtual (and not in umbracoNode). - public static string ContentCacheRouteByContent(int id, bool previewing) + public static string ContentCacheRouteByContent(int id, bool previewing, string language) { - return "NuCache.ContentCache.RouteByContent[" + DraftOrPub(previewing) + id + "]"; + return "NuCache.ContentCache.RouteByContent[" + DraftOrPub(previewing) + id + LangId(language) + "]"; } public static string ContentCacheContentByRoute(string route, bool previewing) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 3907c4c79c..f27d2c9937 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -25,10 +25,11 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly ICacheProvider _elementsCache; private readonly DomainHelper _domainHelper; private readonly IGlobalSettings _globalSettings; + private readonly ILocalizationService _localizationService; #region Constructor - public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, ICacheProvider snapshotCache, ICacheProvider elementsCache, DomainHelper domainHelper, IGlobalSettings globalSettings) + public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, ICacheProvider snapshotCache, ICacheProvider elementsCache, DomainHelper domainHelper, IGlobalSettings globalSettings, ILocalizationService localizationService) : base(previewDefault) { _snapshot = snapshot; @@ -36,6 +37,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _elementsCache = elementsCache; _domainHelper = domainHelper; _globalSettings = globalSettings; + _localizationService = localizationService; } private bool HideTopLevelNodeFromPath => _globalSettings.HideTopLevelNodeFromPath; @@ -118,19 +120,19 @@ namespace Umbraco.Web.PublishedCache.NuCache return content; } - public string GetRouteById(int contentId) + public string GetRouteById(int contentId, string language = null) { - return GetRouteById(PreviewDefault, contentId); + return GetRouteById(PreviewDefault, contentId, language); } - public string GetRouteById(bool preview, int contentId) + public string GetRouteById(bool preview, int contentId, string language = null) { var cache = (preview == false || PublishedSnapshotService.FullCacheWhenPreviewing) ? _elementsCache : _snapshotCache; - var key = CacheKeys.ContentCacheRouteByContent(contentId, preview); - return cache.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null)); + var key = CacheKeys.ContentCacheRouteByContent(contentId, preview, language); + return cache.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null, language)); } - private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode) + private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode, string language) { var node = GetById(preview, contentId); if (node == null) @@ -146,7 +148,23 @@ namespace Umbraco.Web.PublishedCache.NuCache while (hasDomains == false && n != null) // n is null at root { // get the url - var urlName = n.UrlName; + string urlName; + if (n.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) + { + var cultureCode = language != null + ? language + : _localizationService.GetDefaultVariantLanguage()?.IsoCode; + + if (n.CultureNames.TryGetValue(cultureCode, out var cultureName)) + urlName = cultureName.UrlName; + else + urlName = n.UrlName; //couldn't get the value, fallback to invariant ... not sure what else to do + } + else + { + urlName = n.UrlName; + } + pathParts.Add(urlName); // move to parent node diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs index 6fd731cbee..29b252cd92 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs @@ -83,6 +83,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource class ContentDataSerializer : ISerializer { private static readonly DictionaryOfPropertyDataSerializer PropertiesSerializer = new DictionaryOfPropertyDataSerializer(); + private static readonly DictionaryOfCultureVariationSerializer CultureVariationsSerializer = new DictionaryOfCultureVariationSerializer(); public ContentData ReadFrom(Stream stream) { @@ -94,7 +95,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource VersionDate = PrimitiveSerializer.DateTime.ReadFrom(stream), WriterId = PrimitiveSerializer.Int32.ReadFrom(stream), TemplateId = PrimitiveSerializer.Int32.ReadFrom(stream), - Properties = PropertiesSerializer.ReadFrom(stream) + Properties = PropertiesSerializer.ReadFrom(stream), + CultureInfos = CultureVariationsSerializer.ReadFrom(stream) }; } @@ -107,6 +109,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource PrimitiveSerializer.Int32.WriteTo(value.WriterId, stream); PrimitiveSerializer.Int32.WriteTo(value.TemplateId, stream); PropertiesSerializer.WriteTo(value.Properties, stream); + CultureVariationsSerializer.WriteTo(value.CultureInfos, stream); } } @@ -131,6 +134,69 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } */ + private class DictionaryOfCultureVariationSerializer : ISerializer> + { + public IReadOnlyDictionary ReadFrom(Stream stream) + { + var dict = new Dictionary(); + + // read values count + var pcount = PrimitiveSerializer.Int32.ReadFrom(stream); + + // read each property + for (var i = 0; i < pcount; i++) + { + // read lang id + // fixme: This will need to change to string when stephane is done his culture work + var key = PrimitiveSerializer.String.ReadFrom(stream); + + var val = new CultureVariation(); + + // read variation info + //TODO: This is supporting multiple properties but we only have one currently + var type = PrimitiveSerializer.Char.ReadFrom(stream); + switch(type) + { + case 'N': + val.Name = PrimitiveSerializer.String.ReadFrom(stream); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + dict[key] = val; + } + return dict; + } + + private static readonly IReadOnlyDictionary Empty = new Dictionary(); + + public void WriteTo(IReadOnlyDictionary value, Stream stream) + { + var valToSerialize = value; + if (valToSerialize == null) + { + valToSerialize = Empty; + } + + // write values count + PrimitiveSerializer.Int32.WriteTo(valToSerialize.Count, stream); + + // write each name + foreach (var kvp in valToSerialize) + { + // write alias + PrimitiveSerializer.String.WriteTo(kvp.Key, stream); + + // write name + PrimitiveSerializer.Char.WriteTo('N', stream); + PrimitiveSerializer.String.WriteTo(kvp.Value.Name, stream); + //TODO: This is supporting multiple properties but we only have one currently + } + } + + } + private class DictionaryOfPropertyDataSerializer : ISerializer> { public IDictionary ReadFrom(Stream stream) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs index 2b8fd108b2..6841937c14 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs @@ -8,8 +8,14 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { public bool Published { get; set; } + /// + /// The collection of language Id to name for the content item + /// + public IReadOnlyDictionary CultureInfos { get; set; } + public string Name { get; set; } public int VersionId { get; set; } + //TODO: This will not make a lot of sense since we'll have dates for each variant publishing, need to wait on Stephane public DateTime VersionDate { get; set; } public int WriterId { get; set; } public int TemplateId { get; set; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSerializedData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSerializedData.cs new file mode 100644 index 0000000000..b2cb22a74b --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSerializedData.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Umbraco.Web.PublishedCache.NuCache.DataSource +{ + /// + /// The content item 1:M data that is serialized to JSON + /// + internal class ContentSerializedData + { + [JsonProperty("properties")] + public Dictionary PropertyData { get; set; } + + [JsonProperty("cultureData")] + public Dictionary CultureData { get; set; } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs new file mode 100644 index 0000000000..aad416a925 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Umbraco.Web.PublishedCache.NuCache.DataSource +{ + /// + /// Represents the culture variation information on a content item + /// + internal class CultureVariation + { + [JsonProperty("name")] + public string Name { get; set; } + + //TODO: We may want some date stamps here + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs index b4b93e799a..2c4249cd08 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs @@ -190,6 +190,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } else { + var deserialized = DeserializeData(dto.EditData); + d = new ContentData { Name = dto.EditName, @@ -198,7 +200,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource VersionId = dto.VersionId, VersionDate = dto.EditVersionDate, WriterId = dto.EditWriterId, - Properties = DeserializeData(dto.EditData) + Properties = deserialized.PropertyData, + CultureInfos = deserialized.CultureData }; } @@ -212,6 +215,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } else { + var deserialized = DeserializeData(dto.PubData); + p = new ContentData { Name = dto.PubName, @@ -220,7 +225,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource VersionId = dto.VersionId, VersionDate = dto.PubVersionDate, WriterId = dto.PubWriterId, - Properties = DeserializeData(dto.PubData) + Properties = deserialized.PropertyData, + CultureInfos = deserialized.CultureData }; } } @@ -244,6 +250,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource if (dto.EditData == null) throw new Exception("No data for media " + dto.Id); + var deserialized = DeserializeData(dto.EditData); + var p = new ContentData { Name = dto.EditName, @@ -252,7 +260,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource VersionId = dto.VersionId, VersionDate = dto.EditVersionDate, WriterId = dto.CreatorId, // what-else? - Properties = DeserializeData(dto.EditData) + Properties = deserialized.PropertyData, + CultureInfos = deserialized.CultureData }; var n = new ContentNode(dto.Id, dto.Uid, @@ -268,7 +277,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return s; } - private static Dictionary DeserializeData(string data) + private static ContentSerializedData DeserializeData(string data) { // by default JsonConvert will deserialize our numeric values as Int64 // which is bad, because they were Int32 in the database - take care @@ -278,7 +287,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource Converters = new List { new ForceInt32Converter() } }; - return JsonConvert.DeserializeObject>(data, settings); + return JsonConvert.DeserializeObject(data, settings); } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 558580d7c0..acfc5bdd31 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Composing; using Umbraco.Web.Models; @@ -18,6 +19,7 @@ namespace Umbraco.Web.PublishedCache.NuCache internal readonly ContentData _contentData; // internal for ContentNode cloning private readonly string _urlName; + private IReadOnlyDictionary _cultureNames; #region Constructors @@ -44,7 +46,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var cache = GetCurrentSnapshotCache(); return cache == null ? GetProfileNameByIdNoCache(id) - : (string) cache.GetCacheItem(CacheKeys.ProfileName(id), () => GetProfileNameByIdNoCache(id)); + : (string)cache.GetCacheItem(CacheKeys.ProfileName(id), () => GetProfileNameByIdNoCache(id)); } private static string GetProfileNameByIdNoCache(int id) @@ -88,7 +90,7 @@ namespace Umbraco.Web.PublishedCache.NuCache IsPreviewing = true; // clone properties so _isPreviewing is true - PropertiesArray = origin.PropertiesArray.Select(x => (IPublishedProperty) new Property((Property) x, this)).ToArray(); + PropertiesArray = origin.PropertiesArray.Select(x => (IPublishedProperty)new Property((Property)x, this)).ToArray(); } #endregion @@ -157,6 +159,25 @@ namespace Umbraco.Web.PublishedCache.NuCache public override PublishedItemType ItemType => _contentNode.ContentType.ItemType; public override string Name => _contentData.Name; + public override IReadOnlyDictionary CultureNames + { + get + { + if (!ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) + return null; + + if (_cultureNames == null) + { + var d = new Dictionary(); + foreach(var c in _contentData.CultureInfos) + { + d[c.Key] = new PublishedCultureName(c.Value.Name, c.Value.Name.ToUrlSegment()); + } + _cultureNames = d; + } + return _cultureNames; + } + } public override int Level => _contentNode.Level; public override string Path => _contentNode.Path; public override int SortOrder => _contentNode.SortOrder; @@ -205,7 +226,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return GetChildren(); // note: ToArray is important here, we want to cache the result, not the function! - return (IEnumerable) cache.GetCacheItem(ChildrenCacheKey, () => GetChildren().ToArray()); + return (IEnumerable)cache.GetCacheItem(ChildrenCacheKey, () => GetChildren().ToArray()); } } @@ -250,8 +271,8 @@ namespace Umbraco.Web.PublishedCache.NuCache if (cache == null) return base.GetProperty(alias, true); - var key = ((Property) property).RecurseCacheKey; - return (Property) cache.GetCacheItem(key, () => base.GetProperty(alias, true)); + var key = ((Property)property).RecurseCacheKey; + return (Property)cache.GetCacheItem(key, () => base.GetProperty(alias, true)); } public override PublishedContentType ContentType => _contentNode.ContentType; @@ -263,7 +284,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // beware what you use that one for - you don't want to cache its result private ICacheProvider GetAppropriateCache() { - var publishedSnapshot = (PublishedShapshot) _publishedSnapshotAccessor.PublishedSnapshot; + var publishedSnapshot = (PublishedShapshot)_publishedSnapshotAccessor.PublishedSnapshot; var cache = publishedSnapshot == null ? null : ((IsPreviewing == false || PublishedSnapshotService.FullCacheWhenPreviewing) && (ItemType != PublishedItemType.Member) @@ -274,7 +295,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private ICacheProvider GetCurrentSnapshotCache() { - var publishedSnapshot = (PublishedShapshot) _publishedSnapshotAccessor.PublishedSnapshot; + var publishedSnapshot = (PublishedShapshot)_publishedSnapshotAccessor.PublishedSnapshot; return publishedSnapshot?.SnapshotCache; } @@ -311,7 +332,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var cache = GetAppropriateCache(); if (cache == null) return new PublishedContent(this).CreateModel(); - return (IPublishedContent) cache.GetCacheItem(AsPreviewingCacheKey, () => new PublishedContent(this).CreateModel()); + return (IPublishedContent)cache.GetCacheItem(AsPreviewingCacheKey, () => new PublishedContent(this).CreateModel()); } // used by Navigable.Source,... diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 992ac62733..a02405c804 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -207,7 +207,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // plug ContentTypeService.UowRefreshedEntity += OnContentTypeRefreshedEntity; - MediaTypeService.UowRefreshedEntity+= OnMediaTypeRefreshedEntity; + MediaTypeService.UowRefreshedEntity += OnMediaTypeRefreshedEntity; MemberTypeService.UowRefreshedEntity += OnMemberTypeRefreshedEntity; } @@ -721,7 +721,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private void Notify(ContentStore store, ContentTypeCacheRefresher.JsonPayload[] payloads, Action, IEnumerable, IEnumerable, IEnumerable> action) { - var nameOfT = typeof (T).Name; + var nameOfT = typeof(T).Name; var removedIds = new List(); var refreshedIds = new List(); @@ -793,7 +793,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - ((PublishedShapshot) CurrentPublishedShapshot).Resync(); + ((PublishedShapshot)CurrentPublishedShapshot).Resync(); } public override void Notify(DomainCacheRefresher.JsonPayload[] payloads) @@ -993,7 +993,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // a MaxValue to make sure this one runs last, and it should be ok scopeContext.Enlist("Umbraco.Web.PublishedCache.NuCache.PublishedSnapshotService.Resync", () => this, (completed, svc) => { - ((PublishedShapshot) svc.CurrentPublishedShapshot).Resync(); + ((PublishedShapshot)svc.CurrentPublishedShapshot).Resync(); }, int.MaxValue); } @@ -1015,7 +1015,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return new PublishedShapshot.PublishedSnapshotElements { - ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, new DomainHelper(domainCache), _globalSettings), + ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, new DomainHelper(domainCache), _globalSettings, _serviceContext.LocalizationService), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor), DomainCache = domainCache, @@ -1079,7 +1079,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private static bool HasChangesImpactingAllVersions(IContent icontent) { - var content = (Content) icontent; + var content = (Content)icontent; // UpdateDate will be dirty // Published may be dirty if saving a Published entity @@ -1093,7 +1093,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private void OnContentRefreshedEntity(DocumentRepository sender, DocumentRepository.ScopedEntityEventArgs args) { var db = args.Scope.Database; - var content = (Content) args.Entity; + var content = (Content)args.Entity; // always refresh the edited data OnRepositoryRefreshed(db, content, false); @@ -1168,14 +1168,17 @@ namespace Umbraco.Web.PublishedCache.NuCache RebuildMemberDbCache(contentTypeIds: memberTypeIds); } - private static ContentNuDto GetDto(IContentBase content, bool published) + private ContentNuDto GetDto(IContentBase content, bool published) { // should inject these in ctor // BUT for the time being we decide not to support ConvertDbToXml/String //var propertyEditorResolver = PropertyEditorResolver.Current; //var dataTypeService = ApplicationContext.Current.Services.DataTypeService; - var data = new Dictionary(); + //the dictionary that will be serialized + var data = new ContentSerializedData(); + + var propertyData = new Dictionary(); foreach (var prop in content.Properties) { var pdatas = new List(); @@ -1206,9 +1209,28 @@ namespace Umbraco.Web.PublishedCache.NuCache // value = e.ValueEditor.ConvertDbToString(prop, prop.PropertyType, dataTypeService); //} } - data[prop.Alias] = pdatas.ToArray(); + propertyData[prop.Alias] = pdatas.ToArray(); } + data.PropertyData = propertyData; + + var cultureData = new Dictionary(); + if (content.Names != null) + { + //fixme - when this stores a culture name reference we don't need this lookup anymore + var keys = content.Names.Keys; + var langs = _serviceContext.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x.IsoCode); + foreach (var name in content.Names) + { + if (langs.TryGetValue(name.Key, out var found)) + { + cultureData[found] = new CultureVariation { Name = name.Value }; + } + } + } + + data.CultureData = cultureData; + var dto = new ContentNuDto { NodeId = content.Id, diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 0d5e61a683..bf764b0ff0 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -144,6 +144,8 @@ namespace Umbraco.Web.PublishedCache public override string Name => _member.Name; + public override IReadOnlyDictionary CultureNames => throw new NotSupportedException(); + public override string UrlName => throw new NotSupportedException(); public override string DocumentTypeAlias => _member.ContentTypeAlias; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index e44f6864a0..010a220cb2 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -112,7 +112,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return GetByRoute(PreviewDefault, route, hideTopLevelNode); } - public virtual string GetRouteById(bool preview, int contentId) + public virtual string GetRouteById(bool preview, int contentId, string language = null) { // try to get from cache if not previewing var route = preview || _routesCache == null ? null : _routesCache.GetRoute(contentId); @@ -137,9 +137,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return route; } - public string GetRouteById(int contentId) + public string GetRouteById(int contentId, string language = null) { - return GetRouteById(PreviewDefault, contentId); + return GetRouteById(PreviewDefault, contentId, language); } IPublishedContent DetermineIdByRoute(bool preview, string route, bool hideTopLevelNode) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index fc8afe4939..48a63aa8d3 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -747,6 +747,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override int SortOrder => _sortOrder; public override string Name => _name; + + public override IReadOnlyDictionary CultureNames => throw new NotSupportedException(); public override string UrlName => _urlName; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index 0ab219d368..6f9746c57d 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -148,7 +148,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache if (_nodeInitialized == false) InitializeNode(); return _name; } - } + } + + public override IReadOnlyDictionary CultureNames => throw new NotSupportedException(); public override string DocumentTypeAlias { diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 3c72a55ede..7113ac498c 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -4,6 +4,7 @@ using System.Linq; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; @@ -15,12 +16,14 @@ namespace Umbraco.Web.Routing public class AliasUrlProvider : IUrlProvider { private readonly IGlobalSettings _globalSettings; - private readonly IRequestHandlerSection _requestConfig; - - public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSection requestConfig) + private readonly IRequestHandlerSection _requestConfig; + private readonly ILocalizationService _localizationService; + + public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSection requestConfig, ILocalizationService localizationService) { _globalSettings = globalSettings; - _requestConfig = requestConfig; + _requestConfig = requestConfig; + _localizationService = localizationService; } // note - at the moment we seem to accept pretty much anything as an alias @@ -42,7 +45,7 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it should return null. /// - public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode) + public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string language = null) { return null; // we have nothing to say } @@ -67,13 +70,13 @@ namespace Umbraco.Web.Routing if (!FindByUrlAliasEnabled) return Enumerable.Empty(); // we have nothing to say - var node = umbracoContext.ContentCache.GetById(id); - string umbracoUrlName = null; - if (node.HasProperty(Constants.Conventions.Content.UrlAlias)) - umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias); - if (string.IsNullOrWhiteSpace(umbracoUrlName)) - return Enumerable.Empty(); - + var node = umbracoContext.ContentCache.GetById(id); + if (node == null) + return Enumerable.Empty(); + + if (!node.HasProperty(Constants.Conventions.Content.UrlAlias)) + return Enumerable.Empty(); + var domainHelper = new DomainHelper(umbracoContext.PublishedShapshot.Domains); var n = node; @@ -84,18 +87,31 @@ namespace Umbraco.Web.Routing n = n.Parent; domainUris = n == null ? null : domainHelper.DomainsForNode(n.Id, current, false); } - - var path = "/" + umbracoUrlName; - - if (domainUris == null) - { + + if (domainUris == null) + { + var path = "/" + node.Value(Constants.Conventions.Content.UrlAlias); var uri = new Uri(path, UriKind.Relative); - return new[] { UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString() }; - } - - return domainUris - .Select(domainUri => new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path))) - .Select(uri => UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString()); + return new[] { UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString() }; + } + else + { + var result = new List(); + var languageIds = new List(domainUris.Select(x => _localizationService.GetLanguageByCultureCode(x.Culture.Name)?.Id).Where(x => x.HasValue)); + foreach (var langId in languageIds) + { + var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias, languageId: langId); + if (!string.IsNullOrWhiteSpace(umbracoUrlName)) + { + var path = "/" + umbracoUrlName; + + result.AddRange(domainUris + .Select(domainUri => new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path))) + .Select(uri => UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString())); + } + } + return result; + } } #endregion diff --git a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs index 251ddf9cdd..ea7983d77c 100644 --- a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs +++ b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing /// /// /// - public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode) + public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string language = null) { if (umbracoContext == null) return null; if (umbracoContext.PublishedRequest == null) return null; diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 93fde8f0b9..13b75ee623 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -37,13 +37,13 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it should return null. /// - public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode) + public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string language = null) { if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", "current"); // will not use cache if previewing - var route = umbracoContext.ContentCache.GetRouteById(id); + var route = umbracoContext.ContentCache.GetRouteById(id, language); return GetUrlFromRoute(route, umbracoContext, id, current, mode); } diff --git a/src/Umbraco.Web/Routing/IUrlProvider.cs b/src/Umbraco.Web/Routing/IUrlProvider.cs index ae38f6d422..3f6ee4dcc0 100644 --- a/src/Umbraco.Web/Routing/IUrlProvider.cs +++ b/src/Umbraco.Web/Routing/IUrlProvider.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it should return null. /// - string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode); + string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string language = null); /// /// Gets the other urls of a published content. diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 56c04deca1..907eac0bde 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -341,6 +341,8 @@ + + diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index ad1ac877be..bc650b5a78 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -201,7 +201,7 @@ namespace umbraco /// void PopulateElementData(IPublishedContent node) { - foreach(var p in node.Properties) + foreach (var p in node.Properties) { if (_elements.ContainsKey(p.Alias) == false) { @@ -407,7 +407,8 @@ namespace umbraco private readonly string _writerName; private readonly PublishedContentType _contentType; private readonly IPublishedProperty[] _properties; - private readonly IPublishedContent _parent; + private readonly IPublishedContent _parent; + private IReadOnlyDictionary _cultureNames; private PagePublishedContent(int id) { @@ -469,6 +470,31 @@ namespace umbraco public string Name { get { return _inner.Name; } + } + + public IReadOnlyDictionary CultureNames + { + get + { + if (!_inner.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) + return null; + + if (_cultureNames == null) + { + var d = new Dictionary(); + //fixme this will not be necessary when the IContentBase.Names is a string based dictionary + var langs = Current.Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x.IsoCode); + foreach (var c in _inner.Names) + { + if (langs.TryGetValue(c.Key, out var lang)) + { + d[lang] = new PublishedCultureName(c.Value, c.Value.ToUrlSegment()); + } + } + _cultureNames = d; + } + return _cultureNames; + } } public string UrlName From 9044c9328d362e7f2f41cc7c4d92761799fc97e7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 13:07:18 +1000 Subject: [PATCH 22/97] Gets inbound routing working, reduces the amount of non injected dependencies, reduces the amount of DomainHelper instances --- src/Umbraco.Core/Models/DomainExtensions.cs | 19 +++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../PublishedContentCacheTests.cs | 7 +- .../Integration/GetCultureTests.cs | 5 + .../PublishedContentMoreTests.cs | 3 +- .../PublishedContentTestElements.cs | 9 +- .../Routing/ContentFinderByIdTests.cs | 4 +- .../ContentFinderByNiceUrlAndTemplateTests.cs | 8 +- .../Routing/ContentFinderByNiceUrlTests.cs | 14 +- .../ContentFinderByNiceUrlWithDomainsTests.cs | 4 +- .../Routing/DomainsAndCulturesTests.cs | 6 +- .../Routing/NiceUrlProviderTests.cs | 10 +- .../NiceUrlsProviderWithDomainsTests.cs | 14 +- .../Routing/SiteDomainHelperTests.cs | 72 ++++----- .../Routing/UrlsWithNestedDomains.cs | 4 +- .../Scoping/ScopedNuCacheTests.cs | 8 +- .../Security/BackOfficeCookieManagerTests.cs | 13 +- .../TestControllerActivatorBase.cs | 4 +- .../TestHelpers/TestObjects-Mocks.cs | 2 +- .../TestHelpers/TestWithDatabaseBase.cs | 5 +- .../Testing/TestingTests/MockTests.cs | 3 +- ...RenderIndexActionSelectorAttributeTests.cs | 12 +- .../Web/Mvc/SurfaceControllerTests.cs | 5 + .../Web/Mvc/UmbracoViewPageTests.cs | 9 +- .../Web/TemplateUtilitiesTests.cs | 4 +- .../Web/WebExtensionMethodTests.cs | 10 +- .../Cache/CacheRefresherComponent.cs | 1 + src/Umbraco.Web/Composing/Current.cs | 5 +- src/Umbraco.Web/Models/ContentExtensions.cs | 45 +++--- .../Models/Mapping/ContentMapperProfile.cs | 5 +- .../Models/Mapping/ContentUrlResolver.cs | 15 +- .../PublishedCache/IPublishedContentCache.cs | 11 +- .../PublishedCache/NuCache/CacheKeys.cs | 9 +- .../PublishedCache/NuCache/ContentCache.cs | 54 +++---- .../NuCache/PublishedSnapshotService.cs | 12 +- .../XmlPublishedCache/DomainCache.cs | 11 +- .../PublishedContentCache.cs | 15 +- .../PublishedSnapshotService.cs | 16 +- .../XmlPublishedCache/XmlCacheComponent.cs | 2 + src/Umbraco.Web/PublishedContentExtensions.cs | 77 ++++++---- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 9 +- .../Routing/ContentFinderByIdPath.cs | 13 +- .../Routing/ContentFinderByLegacy404.cs | 2 +- ...nderByNiceUrl.cs => ContentFinderByUrl.cs} | 126 ++++++++-------- ...te.cs => ContentFinderByUrlAndTemplate.cs} | 139 +++++++++--------- .../Routing/CustomRouteUrlProvider.cs | 3 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 19 ++- src/Umbraco.Web/Routing/Domain.cs | 9 +- src/Umbraco.Web/Routing/DomainHelper.cs | 23 +-- src/Umbraco.Web/Routing/IUrlProvider.cs | 3 +- src/Umbraco.Web/Routing/SiteDomainHelper.cs | 9 +- src/Umbraco.Web/Routing/UrlProvider.cs | 22 +-- .../Routing/UrlProviderExtensions.cs | 33 ++--- .../Runtime/WebRuntimeComponent.cs | 8 +- src/Umbraco.Web/Umbraco.Web.csproj | 4 +- src/Umbraco.Web/UmbracoContext.cs | 42 ++++-- src/Umbraco.Web/UmbracoModule.cs | 8 +- 57 files changed, 582 insertions(+), 423 deletions(-) create mode 100644 src/Umbraco.Core/Models/DomainExtensions.cs rename src/Umbraco.Web/Routing/{ContentFinderByNiceUrl.cs => ContentFinderByUrl.cs} (82%) rename src/Umbraco.Web/Routing/{ContentFinderByNiceUrlAndTemplate.cs => ContentFinderByUrlAndTemplate.cs} (76%) diff --git a/src/Umbraco.Core/Models/DomainExtensions.cs b/src/Umbraco.Core/Models/DomainExtensions.cs new file mode 100644 index 0000000000..47cd34105b --- /dev/null +++ b/src/Umbraco.Core/Models/DomainExtensions.cs @@ -0,0 +1,19 @@ +using Umbraco.Core.Services; + +namespace Umbraco.Core.Models +{ + public static class DomainExtensions + { + /// + /// Returns ture if the has a culture code equal to the default language specified + /// + /// + /// + /// + public static bool IsDefaultDomain(this IDomain domain, ILocalizationService localizationService) + { + var defaultLang = localizationService.GetDefaultVariantLanguage(); + return domain.LanguageIsoCode == defaultLang.CultureName; + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c8f4ed577d..a8640d4c84 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -348,6 +348,7 @@ + diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index f330c126d0..304c20c78f 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -62,9 +62,9 @@ namespace Umbraco.Tests.Cache.PublishedCache _xml.LoadXml(GetXml()); var xmlStore = new XmlStore(() => _xml, null, null, null); var cacheProvider = new StaticCacheProvider(); - var domainCache = new DomainCache(ServiceContext.DomainService); + var domainCache = new DomainCache(ServiceContext.DomainService, ServiceContext.LocalizationService); var publishedShapshot = new Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedShapshot( - new PublishedContentCache(xmlStore, domainCache, cacheProvider, globalSettings, ContentTypesCache, null, null), + new PublishedContentCache(xmlStore, domainCache, cacheProvider, globalSettings, new SiteDomainHelper(), ContentTypesCache, null, null), new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, cacheProvider, ContentTypesCache), new PublishedMemberCache(null, cacheProvider, Current.Services.MemberService, ContentTypesCache), domainCache); @@ -77,7 +77,8 @@ namespace Umbraco.Tests.Cache.PublishedCache new WebSecurity(_httpContextFactory.HttpContext, Current.Services.UserService, globalSettings), umbracoSettings, Enumerable.Empty(), - globalSettings); + globalSettings, + ServiceContext.EntityService); _cache = _umbracoContext.ContentCache; } diff --git a/src/Umbraco.Tests/Integration/GetCultureTests.cs b/src/Umbraco.Tests/Integration/GetCultureTests.cs index 5441024570..ec3a73b37e 100644 --- a/src/Umbraco.Tests/Integration/GetCultureTests.cs +++ b/src/Umbraco.Tests/Integration/GetCultureTests.cs @@ -54,18 +54,21 @@ namespace Umbraco.Tests.Integration var content = c2; var culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, + new SiteDomainHelper(), content.Id, content.Path, new Uri("http://domain1.com/")); Assert.AreEqual("en-US", culture.Name); content = c2; culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, + new SiteDomainHelper(), content.Id, content.Path, new Uri("http://domain1.fr/")); Assert.AreEqual("fr-FR", culture.Name); content = c4; culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, + new SiteDomainHelper(), content.Id, content.Path, new Uri("http://domain1.fr/")); Assert.AreEqual("de-DE", culture.Name); } @@ -102,12 +105,14 @@ namespace Umbraco.Tests.Integration var content = c2; var culture = Umbraco.Web.Models.ContentExtensions.GetCulture(null, ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, + new SiteDomainHelper(), content.Id, content.Path, new Uri("http://domain1.com/")); Assert.AreEqual("de-DE", culture.Name); content = c4; culture = Umbraco.Web.Models.ContentExtensions.GetCulture(null, ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, + new SiteDomainHelper(), content.Id, content.Path, new Uri("http://domain1.fr/")); Assert.AreEqual("fr-FR", culture.Name); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 2713229a5e..3e715792cb 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -74,7 +74,8 @@ namespace Umbraco.Tests.PublishedContent new WebSecurity(httpContext, Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings); + globalSettings, + ServiceContext.EntityService); return umbracoContext; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs index 9241686188..aa6f62cf46 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Moq; using Umbraco.Core; @@ -58,22 +59,22 @@ namespace Umbraco.Tests.PublishedContent _content.Clear(); } - public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null) + public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, CultureInfo culture = null) { throw new NotImplementedException(); } - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null) + public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, CultureInfo culture = null) { throw new NotImplementedException(); } - public string GetRouteById(bool preview, int contentId, string language = null) + public string GetRouteById(bool preview, int contentId, CultureInfo culture = null) { throw new NotImplementedException(); } - public string GetRouteById(int contentId, string language = null) + public string GetRouteById(int contentId, CultureInfo culture = null) { throw new NotImplementedException(); } diff --git a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs index 61c5b4aae9..2f2edf3964 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs @@ -1,6 +1,8 @@ using NUnit.Framework; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; +using LightInject; namespace Umbraco.Tests.Routing { @@ -15,7 +17,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlAsString); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByIdPath(Logger); + var lookup = new ContentFinderByIdPath(Container.GetInstance().WebRouting, Logger); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs index c58899d4d1..7a4be24fbf 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs @@ -1,11 +1,13 @@ using Moq; -using NUnit.Framework; +using NUnit.Framework; +using LightInject; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; using Umbraco.Core.Models; using Umbraco.Tests.Testing; using Current = Umbraco.Web.Composing.Current; - +using Umbraco.Core.Configuration.UmbracoSettings; + namespace Umbraco.Tests.Routing { [TestFixture] @@ -37,7 +39,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlAsString, template1.Id, globalSettings:globalSettings.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByNiceUrlAndTemplate(Logger); + var lookup = new ContentFinderByUrlAndTemplate(Logger, ServiceContext.FileService); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs index 66f5cc722a..3e8101a212 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByNiceUrl(Logger); + var lookup = new ContentFinderByUrl(Logger); Assert.IsTrue(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); @@ -70,7 +70,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByNiceUrl(Logger); + var lookup = new ContentFinderByUrl(Logger); Assert.IsFalse(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); @@ -96,7 +96,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByNiceUrl(Logger); + var lookup = new ContentFinderByUrl(Logger); var result = lookup.TryFindContent(frequest); @@ -124,8 +124,8 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.Domain = new DomainAndUri(new Domain(1, "mysite", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/")); - var lookup = new ContentFinderByNiceUrl(Logger); + frequest.Domain = new DomainAndUri(new Domain(1, "mysite", -1, CultureInfo.CurrentCulture, false, true), new Uri("http://mysite/")); + var lookup = new ContentFinderByUrl(Logger); var result = lookup.TryFindContent(frequest); @@ -154,8 +154,8 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.Domain = new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/æøå")); - var lookup = new ContentFinderByNiceUrl(Logger); + frequest.Domain = new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false, true), new Uri("http://mysite/æøå")); + var lookup = new ContentFinderByUrl(Logger); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs index 396697001d..2e9bccf398 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs @@ -135,7 +135,7 @@ namespace Umbraco.Tests.Routing // must lookup domain else lookup by url fails publishedRouter.FindDomain(frequest); - var lookup = new ContentFinderByNiceUrl(Logger); + var lookup = new ContentFinderByUrl(Logger); var result = lookup.TryFindContent(frequest); Assert.IsTrue(result); Assert.AreEqual(expectedId, frequest.PublishedContent.Id); @@ -179,7 +179,7 @@ namespace Umbraco.Tests.Routing publishedRouter.FindDomain(frequest); Assert.AreEqual(expectedCulture, frequest.Culture.Name); - var lookup = new ContentFinderByNiceUrl(Logger); + var lookup = new ContentFinderByUrl(Logger); var result = lookup.TryFindContent(frequest); Assert.IsTrue(result); Assert.AreEqual(expectedId, frequest.PublishedContent.Id); diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index b1d024cc8f..1740f1288c 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -277,7 +277,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(expectedCulture, frequest.Culture.Name); - var finder = new ContentFinderByNiceUrl(Logger); + var finder = new ContentFinderByUrl(Logger); var result = finder.TryFindContent(frequest); Assert.IsTrue(result); @@ -326,7 +326,7 @@ namespace Umbraco.Tests.Routing publishedRouter.FindDomain(frequest); // find document - var finder = new ContentFinderByNiceUrl(Logger); + var finder = new ContentFinderByUrl(Logger); var result = finder.TryFindContent(frequest); // apply wildcard domain @@ -378,7 +378,7 @@ namespace Umbraco.Tests.Routing var content = umbracoContext.ContentCache.GetById(nodeId); Assert.IsNotNull(content); - var culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(umbracoContext, domainService, ServiceContext.LocalizationService, null, content.Id, content.Path, new Uri(currentUrl)); + var culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(umbracoContext, domainService, ServiceContext.LocalizationService, null, new SiteDomainHelper(), content.Id, content.Path, new Uri(currentUrl)); Assert.AreEqual(expectedCulture, culture.Name); } } diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs index 246a9beb8a..a4b51bad85 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new [] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); var requestHandlerMock = Mock.Get(_umbracoSettings.RequestHandler); @@ -108,7 +108,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); var requestMock = Mock.Get(_umbracoSettings.RequestHandler); @@ -138,7 +138,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); var requestMock = Mock.Get(_umbracoSettings.RequestHandler); @@ -162,7 +162,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, umbracoSettings: _umbracoSettings, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); Assert.AreEqual("/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); @@ -185,7 +185,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); //mock the Umbraco settings that we need diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs index fa223420f2..938f0cbec6 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs @@ -184,7 +184,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); SetDomains1(); @@ -220,7 +220,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); SetDomains2(); @@ -248,7 +248,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); SetDomains3(); @@ -282,7 +282,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); SetDomains4(); @@ -306,7 +306,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); SetDomains4(); @@ -373,7 +373,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, umbracoSettings: settings, urlProviders: new[] { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); SetDomains4(); @@ -405,7 +405,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, umbracoSettings: settings, urlProviders: new[] { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); SetDomains5(); diff --git a/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs b/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs index 3b149280bd..fea8948b8e 100644 --- a/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs +++ b/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs @@ -188,8 +188,8 @@ namespace Umbraco.Tests.Routing var current = new Uri("https://www.domain1.com/foo/bar"); var domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), - new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false), + new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, true), + new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false, false), }); var output = helper.MapDomain(current, domainAndUris).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); @@ -198,8 +198,8 @@ namespace Umbraco.Tests.Routing current = new Uri("https://domain1.com/foo/bar"); domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false), - new Domain(1, "https://domain2.com", -1, CultureInfo.CurrentCulture, false) + new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false, true), + new Domain(1, "https://domain2.com", -1, CultureInfo.CurrentCulture, false, false) }); output = helper.MapDomain(current, domainAndUris).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); @@ -207,8 +207,8 @@ namespace Umbraco.Tests.Routing current = new Uri("https://domain1.com/foo/bar"); domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false), - new Domain(1, "https://domain4.com", -1, CultureInfo.CurrentCulture, false) + new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false, true), + new Domain(1, "https://domain4.com", -1, CultureInfo.CurrentCulture, false, false) }); output = helper.MapDomain(current, domainAndUris).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); @@ -216,8 +216,8 @@ namespace Umbraco.Tests.Routing current = new Uri("https://domain4.com/foo/bar"); domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false), - new Domain(1, "https://domain4.com", -1, CultureInfo.CurrentCulture, false) + new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false, true), + new Domain(1, "https://domain4.com", -1, CultureInfo.CurrentCulture, false, false) }); output = helper.MapDomain(current, domainAndUris).Uri.ToString(); Assert.AreEqual("https://domain4.com/", output); @@ -243,8 +243,8 @@ namespace Umbraco.Tests.Routing var current = new Uri("http://domain1.com/foo/bar"); var output = helper.MapDomain(current, new[] { - new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false), current), - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), + new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false, true), current), + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), }).Uri.ToString(); Assert.AreEqual("http://domain1.com/", output); @@ -254,8 +254,8 @@ namespace Umbraco.Tests.Routing current = new Uri("http://domain1.com/foo/bar"); output = helper.MapDomain(current, new[] { - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false), current), - new DomainAndUri(new Domain(1, "domain2.net", -1, CultureInfo.CurrentCulture, false), current) + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false, true), current), + new DomainAndUri(new Domain(1, "domain2.net", -1, CultureInfo.CurrentCulture, false, false), current) }).Uri.ToString(); Assert.AreEqual("http://domain1.net/", output); @@ -266,8 +266,8 @@ namespace Umbraco.Tests.Routing current = new Uri("http://domain1.com/foo/bar"); output = helper.MapDomain(current, new[] { - new DomainAndUri(new Domain(1, "domain2.net", -1, CultureInfo.CurrentCulture, false), current), - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false), current) + new DomainAndUri(new Domain(1, "domain2.net", -1, CultureInfo.CurrentCulture, false, true), current), + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false, false), current) }).Uri.ToString(); Assert.AreEqual("http://domain1.net/", output); } @@ -293,11 +293,11 @@ namespace Umbraco.Tests.Routing var current = new Uri("http://domain1.com/foo/bar"); var output = helper.MapDomains(current, new[] { - new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false), current), // no: current + what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false), current), // yes: same site (though bogus setup) + new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false, true), current), // no: current + what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: same site (though bogus setup) }, true).ToArray(); Assert.AreEqual(1, output.Count()); @@ -308,11 +308,11 @@ namespace Umbraco.Tests.Routing current = new Uri("http://domain1.com/foo/bar"); output = helper.MapDomains(current, new[] { - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false), current), // no: what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false), current), // yes: same site (though bogus setup) + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false, true), current), // no: what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: same site (though bogus setup) }, true).ToArray(); Assert.AreEqual(1, output.Count()); @@ -326,12 +326,12 @@ namespace Umbraco.Tests.Routing current = new Uri("http://domain1.com/foo/bar"); output = helper.MapDomains(current, new[] { - new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false), current), // no: current + what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain3.org", -1, CultureInfo.CurrentCulture, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false), current), // yes: same site (though bogus setup) + new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false, true), current), // no: current + what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain3.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: same site (though bogus setup) }, true).ToArray(); Assert.AreEqual(3, output.Count()); @@ -344,12 +344,12 @@ namespace Umbraco.Tests.Routing current = new Uri("http://domain1.com/foo/bar"); output = helper.MapDomains(current, new[] { - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false), current), // no: what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain3.org", -1, CultureInfo.CurrentCulture, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false), current), // yes: same site (though bogus setup) + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false, true), current), // no: what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain3.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: same site (though bogus setup) }, true).ToArray(); Assert.AreEqual(3, output.Count()); diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 41a7bf730d..0996aaab03 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -44,7 +44,7 @@ namespace Umbraco.Tests.Routing // get the nice url for 100111 var umbracoContext = GetUmbracoContext(url, 9999, umbracoSettings: settings, urlProviders: new [] { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); Assert.AreEqual("http://domain2.com/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, true)); @@ -62,7 +62,7 @@ namespace Umbraco.Tests.Routing Assert.IsTrue(frequest.HasDomain); // check that it's been routed - var lookup = new ContentFinderByNiceUrl(Logger); + var lookup = new ContentFinderByUrl(Logger); var result = lookup.TryFindContent(frequest); Assert.IsTrue(result); Assert.AreEqual(100111, frequest.PublishedContent.Id); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 585a943416..bb3b41d128 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -90,7 +90,7 @@ namespace Umbraco.Tests.Scoping publishedSnapshotAccessor, Logger, ScopeProvider, - documentRepository, mediaRepository, memberRepository, Container.GetInstance()); + documentRepository, mediaRepository, memberRepository, Container.GetInstance(), new SiteDomainHelper()); } protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) @@ -100,13 +100,15 @@ namespace Umbraco.Tests.Scoping var httpContext = GetHttpContextFactory(url, routeData).HttpContext; + var globalSettings = TestObjects.GetGlobalSettings(); var umbracoContext = new UmbracoContext( httpContext, service, - new WebSecurity(httpContext, Current.Services.UserService, TestObjects.GetGlobalSettings()), + new WebSecurity(httpContext, Current.Services.UserService, globalSettings), umbracoSettings ?? SettingsForTests.GetDefaultUmbracoSettings(), urlProviders ?? Enumerable.Empty(), - TestObjects.GetGlobalSettings()); + globalSettings, + Mock.Of()); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index 1b182024c3..51d6a7fa7d 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Services; using Umbraco.Tests.Testing; using Umbraco.Web; using Umbraco.Web.PublishedCache; @@ -26,11 +27,13 @@ namespace Umbraco.Tests.Security //should force app ctx to show not-configured ConfigurationManager.AppSettings.Set("umbracoConfigurationStatus", ""); + var globalSettings = TestObjects.GetGlobalSettings(); var umbracoContext = new UmbracoContext( Mock.Of(), Mock.Of(), - new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), - TestObjects.GetUmbracoSettings(), new List(),TestObjects.GetGlobalSettings()); + new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), + TestObjects.GetUmbracoSettings(), new List(),globalSettings, + Mock.Of()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); var mgr = new BackOfficeCookieManager( @@ -44,11 +47,13 @@ namespace Umbraco.Tests.Security [Test] public void ShouldAuthenticateRequest_When_Configured() { + var globalSettings = TestObjects.GetGlobalSettings(); var umbCtx = new UmbracoContext( Mock.Of(), Mock.Of(), - new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), - TestObjects.GetUmbracoSettings(), new List(), TestObjects.GetGlobalSettings()); + new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), + TestObjects.GetUmbracoSettings(), new List(), globalSettings, + Mock.Of()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); var mgr = new BackOfficeCookieManager(Mock.Of(accessor => accessor.UmbracoContext == umbCtx), runtime, TestObjects.GetGlobalSettings()); diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 7a8188cd0b..05e9a39551 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -28,6 +28,7 @@ using Umbraco.Web.Routing; using Umbraco.Web.Security; using Umbraco.Web.WebApi; using LightInject; +using System.Globalization; namespace Umbraco.Tests.TestHelpers.ControllerTesting { @@ -148,10 +149,11 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == UrlProviderMode.Auto.ToString())), Enumerable.Empty(), globalSettings, + mockedEntityService, true); //replace it var urlHelper = new Mock(); - urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var membershipHelper = new MembershipHelper(umbCtx, Mock.Of(), Mock.Of()); diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 50cb115eae..050c10757a 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -119,7 +119,7 @@ namespace Umbraco.Tests.TestHelpers var urlProviders = Enumerable.Empty(); if (accessor == null) accessor = new TestUmbracoContextAccessor(); - return UmbracoContext.EnsureContext(accessor, httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, true); + return UmbracoContext.EnsureContext(accessor, httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, Mock.Of(), true); } public IUmbracoSettingsSection GetUmbracoSettings() diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 9a2828fd9f..2b6dcffeab 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -265,7 +265,7 @@ namespace Umbraco.Tests.TestHelpers cache, publishedSnapshotAccessor, Container.GetInstance(), Container.GetInstance(), Container.GetInstance(), Logger, - Container.GetInstance(), + Container.GetInstance(), new SiteDomainHelper(), ContentTypesCache, null, true, Options.PublishedRepositoryEvents); @@ -366,7 +366,8 @@ namespace Umbraco.Tests.TestHelpers new WebSecurity(httpContext, Container.GetInstance(), Container.GetInstance()), umbracoSettings ?? Container.GetInstance(), urlProviders ?? Enumerable.Empty(), - globalSettings ?? Container.GetInstance()); + globalSettings ?? Container.GetInstance(), + ServiceContext.EntityService); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 580defd5ab..7575bb3d1f 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Web.Security; using Moq; using NUnit.Framework; @@ -73,7 +74,7 @@ namespace Umbraco.Tests.Testing.TestingTests var umbracoContext = TestObjects.GetUmbracoContextMock(); var urlProviderMock = new Mock(); - urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var urlProvider = urlProviderMock.Object; diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs index 1a61882949..1f5fe1a6e3 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -71,7 +71,8 @@ namespace Umbraco.Tests.Web.Mvc new Mock(null, null, globalSettings).Object, TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings, + globalSettings, + Mock.Of(), true); var ctrl = new MatchesDefaultIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -94,7 +95,8 @@ namespace Umbraco.Tests.Web.Mvc new Mock(null, null, globalSettings).Object, TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings, + globalSettings, + Mock.Of(), true); var ctrl = new MatchesOverriddenIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -117,7 +119,8 @@ namespace Umbraco.Tests.Web.Mvc new Mock(null, null, globalSettings).Object, TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings, + globalSettings, + Mock.Of(), true); var ctrl = new MatchesCustomIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -140,7 +143,8 @@ namespace Umbraco.Tests.Web.Mvc new Mock(null, null, globalSettings).Object, TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings, + globalSettings, + Mock.Of(), true); var ctrl = new MatchesAsyncIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index d9a12751c0..927b5557bd 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -45,6 +45,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var ctrl = new TestSurfaceController { UmbracoContext = umbracoContext }; @@ -66,6 +67,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var ctrl = new TestSurfaceController { UmbracoContext = umbCtx }; @@ -85,6 +87,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var controller = new TestSurfaceController { UmbracoContext = umbracoContext }; @@ -111,6 +114,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "AutoLegacy")), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var helper = new UmbracoHelper( @@ -148,6 +152,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == webRoutingSettings), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var content = Mock.Of(publishedContent => publishedContent.Id == 12345); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 053a1f7c5d..9fe3b44264 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -425,17 +425,20 @@ namespace Umbraco.Tests.Web.Mvc var factory = Mock.Of(); _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(), null, null, null, null, - Current.Logger, TestObjects.GetGlobalSettings(), null, true, false); // no events + Current.Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), null, true, false); // no events var http = GetHttpContextFactory(url, routeData).HttpContext; + + var globalSettings = TestObjects.GetGlobalSettings(); var ctx = new UmbracoContext( GetHttpContextFactory(url, routeData).HttpContext, _service, - new WebSecurity(http, Current.Services.UserService, TestObjects.GetGlobalSettings()), + new WebSecurity(http, Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - TestObjects.GetGlobalSettings()); + globalSettings, + Mock.Of()); //if (setSingleton) //{ diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index e6a5dd8438..ed06948e3f 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Web; using LightInject; using Moq; @@ -72,7 +73,7 @@ namespace Umbraco.Tests.Web //setup a mock url provider which we'll use fo rtesting var testUrlProvider = new Mock(); - testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns((UmbracoContext umbCtx, int id, Uri url, UrlProviderMode mode) => { return "/my-test-url"; @@ -90,6 +91,7 @@ namespace Umbraco.Tests.Web //pass in the custom url provider new[]{ testUrlProvider.Object }, globalSettings, + entityService.Object, true)) { var output = TemplateUtilities.ParseInternalLinks(input, umbCtx.UrlProvider); diff --git a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs index 86339c309f..cc97633cde 100644 --- a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs +++ b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs @@ -5,6 +5,7 @@ using System.Web.Mvc; using System.Web.Routing; using Moq; using NUnit.Framework; +using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web; @@ -29,7 +30,8 @@ namespace Umbraco.Tests.Web new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), TestObjects.GetUmbracoSettings(), new List(), - TestObjects.GetGlobalSettings()); + TestObjects.GetGlobalSettings(), + Mock.Of()); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -46,7 +48,8 @@ namespace Umbraco.Tests.Web new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), TestObjects.GetUmbracoSettings(), new List(), - TestObjects.GetGlobalSettings()); + TestObjects.GetGlobalSettings(), + Mock.Of()); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -73,7 +76,8 @@ namespace Umbraco.Tests.Web new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), TestObjects.GetUmbracoSettings(), new List(), - TestObjects.GetGlobalSettings()); + TestObjects.GetGlobalSettings(), + Mock.Of()); var httpContext = Mock.Of(); diff --git a/src/Umbraco.Web/Cache/CacheRefresherComponent.cs b/src/Umbraco.Web/Cache/CacheRefresherComponent.cs index 15030db207..07dcebd763 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherComponent.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherComponent.cs @@ -237,6 +237,7 @@ namespace Umbraco.Web.Cache UmbracoConfig.For.UmbracoSettings(), Current.UrlProviders, UmbracoConfig.For.GlobalSettings(), + Current.Services.EntityService, true); } diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index b819046a9a..5ca7b60cf0 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -142,10 +142,7 @@ namespace Umbraco.Web.Composing internal static IPublishedSnapshotService PublishedSnapshotService => Container.GetInstance(); - - public static ISiteDomainHelper SiteDomainHelper - => Container.GetInstance(); - + public static ThumbnailProviderCollection ThumbnailProviders => Container.GetInstance(); diff --git a/src/Umbraco.Web/Models/ContentExtensions.cs b/src/Umbraco.Web/Models/ContentExtensions.cs index 4a016a895b..0712e2503d 100644 --- a/src/Umbraco.Web/Models/ContentExtensions.cs +++ b/src/Umbraco.Web/Models/ContentExtensions.cs @@ -11,23 +11,27 @@ namespace Umbraco.Web.Models { public static class ContentExtensions { - /// - /// Gets the culture that would be selected to render a specified content, - /// within the context of a specified current request. - /// - /// The content. - /// The request Uri. - /// The culture that would be selected to render the content. - public static CultureInfo GetCulture(this IContent content, Uri current = null) - { - return GetCulture(UmbracoContext.Current, - Current.Services.DomainService, - Current.Services.LocalizationService, - Current.Services.ContentService, - content.Id, content.Path, - current); - } + //TODO: Not used + ///// + ///// Gets the culture that would be selected to render a specified content, + ///// within the context of a specified current request. + ///// + ///// The content. + ///// The request Uri. + ///// The culture that would be selected to render the content. + //public static CultureInfo GetCulture(this IContent content, Uri current = null) + //{ + // return GetCulture(UmbracoContext.Current, + // Current.Services.DomainService, + // Current.Services.LocalizationService, + // Current.Services.ContentService, + // content.Id, content.Path, + // current); + //} + + + //TODO: Not used - only in tests /// /// Gets the culture that would be selected to render a specified content, /// within the context of a specified current request. @@ -42,6 +46,7 @@ namespace Umbraco.Web.Models /// The culture that would be selected to render the content. internal static CultureInfo GetCulture(UmbracoContext umbracoContext, IDomainService domainService, ILocalizationService localizationService, IContentService contentService, + ISiteDomainHelper siteDomainHelper, int contentId, string contentPath, Uri current) { var route = umbracoContext == null @@ -49,9 +54,9 @@ namespace Umbraco.Web.Models : umbracoContext.ContentCache.GetRouteById(contentId); // may be cached var domainCache = umbracoContext == null - ? new PublishedCache.XmlPublishedCache.DomainCache(domainService) // for tests only + ? new PublishedCache.XmlPublishedCache.DomainCache(domainService, localizationService) // for tests only : umbracoContext.PublishedShapshot.Domains; // default - var domainHelper = new DomainHelper(domainCache); + var domainHelper = umbracoContext.GetDomainHelper(siteDomainHelper); Domain domain; if (route == null) @@ -59,6 +64,8 @@ namespace Umbraco.Web.Models // if content is not published then route is null and we have to work // on non-published content (note: could optimize by checking routes?) + // fixme - even non-published content is stored in the cache or in the cmsContentNu table which would be faster to lookup + var content = contentService.GetById(contentId); if (content == null) return GetDefaultCulture(localizationService); @@ -93,7 +100,7 @@ namespace Umbraco.Web.Models private static CultureInfo GetDefaultCulture(ILocalizationService localizationService) { - var defaultLanguage = localizationService.GetAllLanguages().FirstOrDefault(); + var defaultLanguage = localizationService.GetDefaultVariantLanguage(); return defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode); } diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 627f508906..059dd32499 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -1,6 +1,7 @@ using System.Linq; using AutoMapper; using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; @@ -13,7 +14,7 @@ namespace Umbraco.Web.Models.Mapping /// internal class ContentMapperProfile : Profile { - public ContentMapperProfile(IUserService userService, ILocalizedTextService textService, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, ILocalizationService localizationService) + public ContentMapperProfile(IUserService userService, ILocalizedTextService textService, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, ILocalizationService localizationService, ILogger logger) { // create, capture, cache var contentOwnerResolver = new OwnerResolver(userService); @@ -24,7 +25,7 @@ namespace Umbraco.Web.Models.Mapping var contentTypeBasicResolver = new ContentTypeBasicResolver(); var contentTreeNodeUrlResolver = new ContentTreeNodeUrlResolver(); var defaultTemplateResolver = new DefaultTemplateResolver(); - var contentUrlResolver = new ContentUrlResolver(); + var contentUrlResolver = new ContentUrlResolver(textService, contentService, logger); var variantResolver = new ContentItemDisplayVariationResolver(localizationService); //FROM IContent TO ContentItemDisplay diff --git a/src/Umbraco.Web/Models/Mapping/ContentUrlResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentUrlResolver.cs index 909ca83985..e311190d67 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentUrlResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentUrlResolver.cs @@ -1,6 +1,8 @@ using System.Linq; using AutoMapper; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Routing; @@ -8,13 +10,24 @@ namespace Umbraco.Web.Models.Mapping { internal class ContentUrlResolver : IValueResolver { + private readonly ILocalizedTextService _textService; + private readonly IContentService _contentService; + private readonly ILogger _logger; + + public ContentUrlResolver(ILocalizedTextService textService, IContentService contentService, ILogger logger) + { + _textService = textService; + _contentService = contentService; + _logger = logger; + } + public string[] Resolve(IContent source, ContentItemDisplay destination, string[] destMember, ResolutionContext context) { var umbracoContext = context.GetUmbracoContext(throwIfMissing: false); var urls = umbracoContext == null ? new[] {"Cannot generate urls without a current Umbraco Context"} - : source.GetContentUrls(umbracoContext).ToArray(); + : source.GetContentUrls(umbracoContext.UrlProvider, _textService, _contentService, _logger).ToArray(); return urls; } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs index 64fa5efde4..2d7c93d93e 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models; +using System.Globalization; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.PublishedCache @@ -17,7 +18,7 @@ namespace Umbraco.Web.PublishedCache /// If is null then the settings value is used. /// The value of overrides defaults. /// - IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null); + IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, CultureInfo culture = null); /// /// Gets content identified by a route. @@ -30,7 +31,7 @@ namespace Umbraco.Web.PublishedCache /// If is null then the settings value is used. /// Considers published or unpublished content depending on defaults. /// - IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null); + IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, CultureInfo culture = null); /// /// Gets the route for a content identified by its unique identifier. @@ -39,7 +40,7 @@ namespace Umbraco.Web.PublishedCache /// The content unique identifier. /// The route. /// The value of overrides defaults. - string GetRouteById(bool preview, int contentId, string language = null); + string GetRouteById(bool preview, int contentId, CultureInfo culture = null); /// /// Gets the route for a content identified by its unique identifier. @@ -47,6 +48,6 @@ namespace Umbraco.Web.PublishedCache /// The content unique identifier. /// The route. /// Considers published or unpublished content depending on defaults. - string GetRouteById(int contentId, string language = null); + string GetRouteById(int contentId, CultureInfo culture = null); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs index 6d21fedb6d..9d887f4d40 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Runtime.CompilerServices; namespace Umbraco.Web.PublishedCache.NuCache @@ -12,9 +13,9 @@ namespace Umbraco.Web.PublishedCache.NuCache } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string LangId(string language) + private static string LangId(CultureInfo culture) { - return language != null ? ("-L:" + language) : string.Empty; + return culture != null ? ("-L:" + culture.Name) : string.Empty; } public static string PublishedContentChildren(Guid contentUid, bool previewing) @@ -56,9 +57,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // a valid ID in the database at that point, whereas content and properties // may be virtual (and not in umbracoNode). - public static string ContentCacheRouteByContent(int id, bool previewing, string language) + public static string ContentCacheRouteByContent(int id, bool previewing, CultureInfo culture) { - return "NuCache.ContentCache.RouteByContent[" + DraftOrPub(previewing) + id + LangId(language) + "]"; + return "NuCache.ContentCache.RouteByContent[" + DraftOrPub(previewing) + id + LangId(culture) + "]"; } public static string ContentCacheContentByRoute(string route, bool previewing) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index f27d2c9937..74598186f6 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -55,21 +55,21 @@ namespace Umbraco.Web.PublishedCache.NuCache // at the moment we try our best to be backward compatible, but really, // should get rid of hideTopLevelNode and other oddities entirely, eventually - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null) + public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, CultureInfo culture = null) { - return GetByRoute(PreviewDefault, route, hideTopLevelNode); + return GetByRoute(PreviewDefault, route, hideTopLevelNode, culture); } - public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null) + public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, CultureInfo culture = null) { if (route == null) throw new ArgumentNullException(nameof(route)); var cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing ? _elementsCache : _snapshotCache; var key = CacheKeys.ContentCacheContentByRoute(route, preview); - return cache.GetCacheItem(key, () => GetByRouteInternal(preview, route, hideTopLevelNode)); + return cache.GetCacheItem(key, () => GetByRouteInternal(preview, route, hideTopLevelNode, culture)); } - private IPublishedContent GetByRouteInternal(bool preview, string route, bool? hideTopLevelNode) + private IPublishedContent GetByRouteInternal(bool preview, string route, bool? hideTopLevelNode, CultureInfo culture) { hideTopLevelNode = hideTopLevelNode ?? HideTopLevelNodeFromPath; // default = settings @@ -90,7 +90,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // note: if domain has a path (eg example.com/en) which is not recommended anymore // then then /en part of the domain is basically ignored here... content = GetById(preview, startNodeId); - content = FollowRoute(content, parts, 0); + content = FollowRoute(content, parts, 0, culture); } else if (parts.Length == 0) { @@ -106,7 +106,7 @@ namespace Umbraco.Web.PublishedCache.NuCache content = hideTopLevelNode.Value ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.UrlName == parts[0]) : GetAtRoot(preview).FirstOrDefault(x => x.UrlName == parts[0]); - content = FollowRoute(content, parts, 1); + content = FollowRoute(content, parts, 1, culture); } // if hideTopLevelNodePath is true then for url /foo we looked for /*/foo @@ -120,19 +120,19 @@ namespace Umbraco.Web.PublishedCache.NuCache return content; } - public string GetRouteById(int contentId, string language = null) + public string GetRouteById(int contentId, CultureInfo culture = null) { - return GetRouteById(PreviewDefault, contentId, language); + return GetRouteById(PreviewDefault, contentId, culture); } - public string GetRouteById(bool preview, int contentId, string language = null) + public string GetRouteById(bool preview, int contentId, CultureInfo culture = null) { var cache = (preview == false || PublishedSnapshotService.FullCacheWhenPreviewing) ? _elementsCache : _snapshotCache; - var key = CacheKeys.ContentCacheRouteByContent(contentId, preview, language); - return cache.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null, language)); + var key = CacheKeys.ContentCacheRouteByContent(contentId, preview, culture); + return cache.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null, culture)); } - private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode, string language) + private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode, CultureInfo culture) { var node = GetById(preview, contentId); if (node == null) @@ -147,23 +147,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var hasDomains = _domainHelper.NodeHasDomains(n.Id); while (hasDomains == false && n != null) // n is null at root { - // get the url - string urlName; - if (n.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) - { - var cultureCode = language != null - ? language - : _localizationService.GetDefaultVariantLanguage()?.IsoCode; - - if (n.CultureNames.TryGetValue(cultureCode, out var cultureName)) - urlName = cultureName.UrlName; - else - urlName = n.UrlName; //couldn't get the value, fallback to invariant ... not sure what else to do - } - else - { - urlName = n.UrlName; - } + var urlName = n.GetUrlName(_localizationService, culture); pathParts.Add(urlName); @@ -184,13 +168,17 @@ namespace Umbraco.Web.PublishedCache.NuCache return route; } - private static IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList parts, int start) + private IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList parts, int start, CultureInfo culture) { var i = start; while (content != null && i < parts.Count) { var part = parts[i++]; - content = content.Children.FirstOrDefault(x => x.UrlName == part); + content = content.Children.FirstOrDefault(x => + { + var urlName = x.GetUrlName(_localizationService, culture); + return urlName == part; + }); } return content; } @@ -266,7 +254,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return GetAtRootNoCache(preview); // note: ToArray is important here, we want to cache the result, not the function! - return (IEnumerable) cache.GetCacheItem( + return (IEnumerable)cache.GetCacheItem( CacheKeys.ContentCacheRoots(preview), () => GetAtRootNoCache(preview).ToArray()); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index a02405c804..eae0fd50b5 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -43,6 +43,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IMediaRepository _mediaRepository; private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; + private readonly ISiteDomainHelper _siteDomainHelper; // volatile because we read it with no lock private volatile bool _isReady; @@ -81,7 +82,8 @@ namespace Umbraco.Web.PublishedCache.NuCache public PublishedSnapshotService(Options options, MainDom mainDom, IRuntimeState runtime, ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap, IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, IScopeProvider scopeProvider, - IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings) + IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, + IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) : base(publishedSnapshotAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) @@ -96,6 +98,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _mediaRepository = mediaRepository; _memberRepository = memberRepository; _globalSettings = globalSettings; + _siteDomainHelper = siteDomainHelper; // we always want to handle repository events, configured or not // assuming no repository event will trigger before the whole db is ready @@ -484,7 +487,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var domains = _serviceContext.DomainService.GetAll(true); foreach (var domain in domains .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard))) + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard, x.IsDefaultDomain(_serviceContext.LocalizationService)))) { _domainStore.Set(domain.Id, domain); } @@ -828,7 +831,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (domain.RootContentId.HasValue == false) continue; // anomaly if (domain.LanguageIsoCode.IsNullOrWhiteSpace()) continue; // anomaly var culture = CultureInfo.GetCultureInfo(domain.LanguageIsoCode); - _domainStore.Set(domain.Id, new Domain(domain.Id, domain.DomainName, domain.RootContentId.Value, culture, domain.IsWildcard)); + _domainStore.Set(domain.Id, new Domain(domain.Id, domain.DomainName, domain.RootContentId.Value, culture, domain.IsWildcard, domain.IsDefaultDomain(_serviceContext.LocalizationService))); break; } } @@ -1012,10 +1015,11 @@ namespace Umbraco.Web.PublishedCache.NuCache var memberTypeCache = new PublishedContentTypeCache(null, null, _serviceContext.MemberTypeService, _publishedContentTypeFactory, _logger); var domainCache = new DomainCache(domainSnap); + var domainHelper = new DomainHelper(domainCache, _siteDomainHelper); return new PublishedShapshot.PublishedSnapshotElements { - ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, new DomainHelper(domainCache), _globalSettings, _serviceContext.LocalizationService), + ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainHelper, _globalSettings, _serviceContext.LocalizationService), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor), DomainCache = domainCache, diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs index 83651a9986..051c333762 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs @@ -4,30 +4,33 @@ using System.Linq; using Umbraco.Web.Routing; using Umbraco.Core; using Umbraco.Core.Services; +using Umbraco.Core.Models; namespace Umbraco.Web.PublishedCache.XmlPublishedCache { - class DomainCache : IDomainCache + internal class DomainCache : IDomainCache { private readonly IDomainService _domainService; + private readonly ILocalizationService _localizationService; - public DomainCache(IDomainService domainService) + public DomainCache(IDomainService domainService, ILocalizationService localizationService) { _domainService = domainService; + _localizationService = localizationService; } public IEnumerable GetAll(bool includeWildcards) { return _domainService.GetAll(includeWildcards) .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard)); + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard, x.IsDefaultDomain(_localizationService))); } public IEnumerable GetAssigned(int contentId, bool includeWildcards) { return _domainService.GetAssignedDomains(contentId, includeWildcards) .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard)); + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard, x.IsDefaultDomain(_localizationService))); } } } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index 010a220cb2..01ab37554e 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -31,7 +31,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache XmlStore xmlStore, // an XmlStore containing the master xml IDomainCache domainCache, // an IDomainCache implementation ICacheProvider cacheProvider, // an ICacheProvider that should be at request-level - IGlobalSettings globalSettings, + IGlobalSettings globalSettings, + ISiteDomainHelper siteDomainHelper, PublishedContentTypeCache contentTypeCache, // a PublishedContentType cache RoutesCache routesCache, // a RoutesCache string previewToken) // a preview token string (or null if not previewing) @@ -42,7 +43,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _routesCache = routesCache; // may be null for unit-testing _contentTypeCache = contentTypeCache; _domainCache = domainCache; - _domainHelper = new DomainHelper(_domainCache); + _domainHelper = new DomainHelper(_domainCache, siteDomainHelper); _xmlStore = xmlStore; _xml = _xmlStore.Xml; // capture - because the cache has to remain consistent @@ -63,7 +64,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache #region Routes - public virtual IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null) + public virtual IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, CultureInfo culture = null) { if (route == null) throw new ArgumentNullException(nameof(route)); @@ -107,12 +108,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _routesCache.Store(content.Id, route, true); // trusted route } - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null) + public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, CultureInfo culture = null) { return GetByRoute(PreviewDefault, route, hideTopLevelNode); } - public virtual string GetRouteById(bool preview, int contentId, string language = null) + public virtual string GetRouteById(bool preview, int contentId, CultureInfo culture = null) { // try to get from cache if not previewing var route = preview || _routesCache == null ? null : _routesCache.GetRoute(contentId); @@ -137,9 +138,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return route; } - public string GetRouteById(int contentId, string language = null) + public string GetRouteById(int contentId, CultureInfo culture = null) { - return GetRouteById(PreviewDefault, contentId, language); + return GetRouteById(PreviewDefault, contentId, culture); } IPublishedContent DetermineIdByRoute(bool preview, string route, bool hideTopLevelNode) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs index fedff54c29..7cde462dbc 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Cache; +using Umbraco.Web.Routing; namespace Umbraco.Web.PublishedCache.XmlPublishedCache { @@ -31,6 +32,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly IUserService _userService; private readonly ICacheProvider _requestCache; private readonly IGlobalSettings _globalSettings; + private readonly ILocalizationService _localizationService; + private readonly ISiteDomainHelper _siteDomainHelper; #region Constructors @@ -44,11 +47,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ILogger logger, IGlobalSettings globalSettings, + ISiteDomainHelper siteDomainHelper, MainDom mainDom, bool testing = false, bool enableRepositoryEvents = true) : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, publishedSnapshotAccessor, documentRepository, mediaRepository, memberRepository, - logger, globalSettings, null, mainDom, testing, enableRepositoryEvents) + logger, globalSettings, siteDomainHelper, null, mainDom, testing, enableRepositoryEvents) { } // used in some tests @@ -60,12 +64,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ILogger logger, IGlobalSettings globalSettings, + ISiteDomainHelper siteDomainHelper, PublishedContentTypeCache contentTypeCache, MainDom mainDom, bool testing, bool enableRepositoryEvents) : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), publishedSnapshotAccessor, documentRepository, mediaRepository, memberRepository, - logger, globalSettings, contentTypeCache, mainDom, testing, enableRepositoryEvents) + logger, globalSettings, siteDomainHelper, contentTypeCache, mainDom, testing, enableRepositoryEvents) { } private PublishedSnapshotService(ServiceContext serviceContext, @@ -77,6 +82,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ILogger logger, IGlobalSettings globalSettings, + ISiteDomainHelper siteDomainHelper, PublishedContentTypeCache contentTypeCache, MainDom mainDom, bool testing, bool enableRepositoryEvents) @@ -95,9 +101,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _memberService = serviceContext.MemberService; _mediaService = serviceContext.MediaService; _userService = serviceContext.UserService; + _localizationService = serviceContext.LocalizationService; _requestCache = requestCache; _globalSettings = globalSettings; + _siteDomainHelper = siteDomainHelper; } public override void Dispose() @@ -138,10 +146,10 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // the current caches, but that would mean creating an extra cache (StaticCache // probably) so better use RequestCache. - var domainCache = new DomainCache(_domainService); + var domainCache = new DomainCache(_domainService, _localizationService); return new PublishedShapshot( - new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _contentTypeCache, _routesCache, previewToken), + new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _siteDomainHelper, _contentTypeCache, _routesCache, previewToken), new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache), new PublishedMemberCache(_xmlStore, _requestCache, _memberService, _contentTypeCache), domainCache); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs index 00bb705c1d..37f29cc1d6 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs @@ -10,6 +10,7 @@ using LightInject; using Umbraco.Core.Configuration; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; +using Umbraco.Web.Routing; namespace Umbraco.Web.PublishedCache.XmlPublishedCache { @@ -33,6 +34,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), factory.GetInstance())); // add the Xml cache health check (hidden from type finder) diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 7bdd196f20..e68365416d 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -272,8 +272,8 @@ namespace Umbraco.Web { //TODO: we should pass in the IExamineManager? - var s = searchProvider ?? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); - + var s = searchProvider ?? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); + var results = s.Search(criteria); return results.ToPublishedSearchResults(UmbracoContext.Current.ContentCache); } @@ -1066,7 +1066,7 @@ namespace Umbraco.Web /// The first child of content, of the given content type. public static IPublishedContent FirstChild(this IPublishedContent content, string alias) { - return content.Children( alias ).FirstOrDefault(); + return content.Children(alias).FirstOrDefault(); } public static IPublishedContent FirstChild(this IPublishedContent content, Func predicate) @@ -1116,12 +1116,12 @@ namespace Umbraco.Web return new DataTable(); //no children found //use new utility class to create table so that we don't have to maintain code in many places, just one - var dt = Core.DataTableExtensions.GenerateDataTable( - //pass in the alias of the first child node since this is the node type we're rendering headers for - firstNode.DocumentTypeAlias, - //pass in the callback to extract the Dictionary of all defined aliases to their names - alias => GetPropertyAliasesAndNames(services, alias), - //pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place. + var dt = Core.DataTableExtensions.GenerateDataTable( + //pass in the alias of the first child node since this is the node type we're rendering headers for + firstNode.DocumentTypeAlias, + //pass in the callback to extract the Dictionary of all defined aliases to their names + alias => GetPropertyAliasesAndNames(services, alias), + //pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place. () => { //create all row data @@ -1222,28 +1222,51 @@ namespace Umbraco.Web private static Dictionary GetAliasesAndNames(IContentTypeBase contentType) { return contentType.PropertyTypes.ToDictionary(x => x.Alias, x => x.Name); - } - + } + #endregion - + #region Culture - + + //TODO: Not used + ///// + ///// Gets the culture that would be selected to render a specified content, + ///// within the context of a specified current request. + ///// + ///// The content. + ///// The request Uri. + ///// The culture that would be selected to render the content. + //public static CultureInfo GetCulture(this IPublishedContent content, Uri current = null) + //{ + // return Models.ContentExtensions.GetCulture(UmbracoContext.Current, + // Current.Services.DomainService, + // Current.Services.LocalizationService, + // Current.Services.ContentService, + // content.Id, content.Path, + // current); + //} + /// - /// Gets the culture that would be selected to render a specified content, - /// within the context of a specified current request. + /// Return the URL name for the based on the culture specified or default culture defined /// - /// The content. - /// The request Uri. - /// The culture that would be selected to render the content. - public static CultureInfo GetCulture(this IPublishedContent content, Uri current = null) - { - return Models.ContentExtensions.GetCulture(UmbracoContext.Current, - Current.Services.DomainService, - Current.Services.LocalizationService, - Current.Services.ContentService, - content.Id, content.Path, - current); - } + /// + /// + /// + /// + public static string GetUrlName(this IPublishedContent content, ILocalizationService localizationService, CultureInfo culture = null) + { + if (content.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) + { + var cultureCode = culture != null ? culture.Name : localizationService.GetDefaultVariantLanguage()?.IsoCode; + if (cultureCode != null && content.CultureNames.TryGetValue(cultureCode, out var cultureName)) + { + return cultureName.UrlName; + } + } + + //if we get here, the content type is invariant or we don't have access to a usable culture code + return content.UrlName; + } #endregion } diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 7113ac498c..7c926f04f3 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Umbraco.Core; using Umbraco.Core.Configuration; @@ -18,12 +19,14 @@ namespace Umbraco.Web.Routing private readonly IGlobalSettings _globalSettings; private readonly IRequestHandlerSection _requestConfig; private readonly ILocalizationService _localizationService; + private readonly ISiteDomainHelper _siteDomainHelper; - public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSection requestConfig, ILocalizationService localizationService) + public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSection requestConfig, ILocalizationService localizationService, ISiteDomainHelper siteDomainHelper) { _globalSettings = globalSettings; _requestConfig = requestConfig; _localizationService = localizationService; + _siteDomainHelper = siteDomainHelper; } // note - at the moment we seem to accept pretty much anything as an alias @@ -45,7 +48,7 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it should return null. /// - public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string language = null) + public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, CultureInfo culture = null) { return null; // we have nothing to say } @@ -77,7 +80,7 @@ namespace Umbraco.Web.Routing if (!node.HasProperty(Constants.Conventions.Content.UrlAlias)) return Enumerable.Empty(); - var domainHelper = new DomainHelper(umbracoContext.PublishedShapshot.Domains); + var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); var n = node; var domainUris = domainHelper.DomainsForNode(n.Id, current, false); diff --git a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs index eb3e481a7b..2052a198bf 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs @@ -16,16 +16,11 @@ namespace Umbraco.Web.Routing { private readonly ILogger _logger; private readonly IWebRoutingSection _webRoutingSection; - - public ContentFinderByIdPath(ILogger logger) - : this(UmbracoConfig.For.UmbracoSettings().WebRouting) + + public ContentFinderByIdPath(IWebRoutingSection webRoutingSection, ILogger logger) { - _logger = logger; - } - - public ContentFinderByIdPath(IWebRoutingSection webRoutingSection) - { - _webRoutingSection = webRoutingSection; + _webRoutingSection = webRoutingSection ?? throw new System.ArgumentNullException(nameof(webRoutingSection)); + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); } /// diff --git a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs index a687d43dbe..090b7d421d 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web.Routing while (pos > 1) { route = route.Substring(0, pos); - node = frequest.UmbracoContext.ContentCache.GetByRoute(route); + node = frequest.UmbracoContext.ContentCache.GetByRoute(route, culture: frequest.Culture); if (node != null) break; pos = route.LastIndexOf('/'); } diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs similarity index 82% rename from src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs rename to src/Umbraco.Web/Routing/ContentFinderByUrl.cs index 094c0addaf..66b8024c6b 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs @@ -1,63 +1,63 @@ -using Umbraco.Core.Logging; -using Umbraco.Core; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Web.Routing -{ - /// - /// Provides an implementation of that handles page nice urls. - /// - /// - /// Handles /foo/bar where /foo/bar is the nice url of a document. - /// - public class ContentFinderByNiceUrl : IContentFinder - { - protected ILogger Logger { get; } - - public ContentFinderByNiceUrl(ILogger logger) - { - Logger = logger; - } - - /// - /// Tries to find and assign an Umbraco document to a PublishedContentRequest. - /// - /// The PublishedContentRequest. - /// A value indicating whether an Umbraco document was found and assigned. - public virtual bool TryFindContent(PublishedRequest frequest) - { - string route; - if (frequest.HasDomain) - route = frequest.Domain.ContentId + DomainHelper.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded()); - else - route = frequest.Uri.GetAbsolutePathDecoded(); - - var node = FindContent(frequest, route); - return node != null; - } - - /// - /// Tries to find an Umbraco document for a PublishedContentRequest and a route. - /// - /// The document request. - /// The route. - /// The document node, or null. - protected IPublishedContent FindContent(PublishedRequest docreq, string route) - { - Logger.Debug(() => $"Test route \"{route}\""); - - var node = docreq.UmbracoContext.ContentCache.GetByRoute(route); - if (node != null) - { - docreq.PublishedContent = node; - Logger.Debug(() => $"Got content, id={node.Id}"); - } - else - { - Logger.Debug("No match."); - } - - return node; - } - } -} +using Umbraco.Core.Logging; +using Umbraco.Core; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Web.Routing +{ + /// + /// Provides an implementation of that handles page nice urls. + /// + /// + /// Handles /foo/bar where /foo/bar is the nice url of a document. + /// + public class ContentFinderByUrl : IContentFinder + { + protected ILogger Logger { get; } + + public ContentFinderByUrl(ILogger logger) + { + Logger = logger; + } + + /// + /// Tries to find and assign an Umbraco document to a PublishedContentRequest. + /// + /// The PublishedContentRequest. + /// A value indicating whether an Umbraco document was found and assigned. + public virtual bool TryFindContent(PublishedRequest frequest) + { + string route; + if (frequest.HasDomain) + route = frequest.Domain.ContentId + DomainHelper.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded()); + else + route = frequest.Uri.GetAbsolutePathDecoded(); + + var node = FindContent(frequest, route); + return node != null; + } + + /// + /// Tries to find an Umbraco document for a PublishedContentRequest and a route. + /// + /// The document request. + /// The route. + /// The document node, or null. + protected IPublishedContent FindContent(PublishedRequest docreq, string route) + { + Logger.Debug(() => $"Test route \"{route}\""); + + var node = docreq.UmbracoContext.ContentCache.GetByRoute(route, culture: docreq.Culture); + if (node != null) + { + docreq.PublishedContent = node; + Logger.Debug(() => $"Got content, id={node.Id}"); + } + else + { + Logger.Debug("No match."); + } + + return node; + } + } +} diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs similarity index 76% rename from src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs rename to src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs index d0eaa55b79..8f507078b3 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs @@ -1,67 +1,72 @@ -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.Routing -{ - /// - /// Provides an implementation of that handles page nice urls and a template. - /// - /// - /// Handles /foo/bar/template where /foo/bar is the nice url of a document, and template a template alias. - /// If successful, then the template of the document request is also assigned. - /// - public class ContentFinderByNiceUrlAndTemplate : ContentFinderByNiceUrl - { - public ContentFinderByNiceUrlAndTemplate(ILogger logger) - : base(logger) - { } - - /// - /// Tries to find and assign an Umbraco document to a PublishedContentRequest. - /// - /// The PublishedContentRequest. - /// A value indicating whether an Umbraco document was found and assigned. - /// If successful, also assigns the template. - public override bool TryFindContent(PublishedRequest frequest) - { - IPublishedContent node = null; - var path = frequest.Uri.GetAbsolutePathDecoded(); - - if (frequest.HasDomain) - path = DomainHelper.PathRelativeToDomain(frequest.Domain.Uri, path); - - if (path != "/") // no template if "/" - { - var pos = path.LastIndexOf('/'); - var templateAlias = path.Substring(pos + 1); - path = pos == 0 ? "/" : path.Substring(0, pos); - - var template = Current.Services.FileService.GetTemplate(templateAlias); - if (template != null) - { - Logger.Debug(() => $"Valid template: \"{templateAlias}\""); - - var route = frequest.HasDomain ? (frequest.Domain.ContentId.ToString() + path) : path; - node = FindContent(frequest, route); - - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false && node != null) - frequest.TemplateModel = template; - } - else - { - Logger.Debug(() => $"Not a valid template: \"{templateAlias}\""); - } - } - else - { - Logger.Debug("No template in path \"/\""); - } - - return node != null; - } - } -} +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Composing; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Routing +{ + /// + /// Provides an implementation of that handles page nice urls and a template. + /// + /// + /// Handles /foo/bar/template where /foo/bar is the nice url of a document, and template a template alias. + /// If successful, then the template of the document request is also assigned. + /// + public class ContentFinderByUrlAndTemplate : ContentFinderByUrl + { + private readonly IFileService _fileService; + + public ContentFinderByUrlAndTemplate(ILogger logger, IFileService fileService) + : base(logger) + { + _fileService = fileService; + } + + /// + /// Tries to find and assign an Umbraco document to a PublishedContentRequest. + /// + /// The PublishedContentRequest. + /// A value indicating whether an Umbraco document was found and assigned. + /// If successful, also assigns the template. + public override bool TryFindContent(PublishedRequest frequest) + { + IPublishedContent node = null; + var path = frequest.Uri.GetAbsolutePathDecoded(); + + if (frequest.HasDomain) + path = DomainHelper.PathRelativeToDomain(frequest.Domain.Uri, path); + + if (path != "/") // no template if "/" + { + var pos = path.LastIndexOf('/'); + var templateAlias = path.Substring(pos + 1); + path = pos == 0 ? "/" : path.Substring(0, pos); + + var template = _fileService.GetTemplate(templateAlias); + if (template != null) + { + Logger.Debug(() => $"Valid template: \"{templateAlias}\""); + + var route = frequest.HasDomain ? (frequest.Domain.ContentId.ToString() + path) : path; + node = FindContent(frequest, route); + + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false && node != null) + frequest.TemplateModel = template; + } + else + { + Logger.Debug(() => $"Not a valid template: \"{templateAlias}\""); + } + } + else + { + Logger.Debug("No template in path \"/\""); + } + + return node != null; + } + } +} diff --git a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs index ea7983d77c..7a3d8fffed 100644 --- a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs +++ b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; namespace Umbraco.Web.Routing { @@ -18,7 +19,7 @@ namespace Umbraco.Web.Routing /// /// /// - public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string language = null) + public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, CultureInfo culture = null) { if (umbracoContext == null) return null; if (umbracoContext.PublishedRequest == null) return null; diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 13b75ee623..d2f0c2662b 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; @@ -14,13 +15,15 @@ namespace Umbraco.Web.Routing { private readonly IRequestHandlerSection _requestSettings; private readonly ILogger _logger; - private readonly IGlobalSettings _globalSettings; - - public DefaultUrlProvider(IRequestHandlerSection requestSettings, ILogger logger, IGlobalSettings globalSettings) + private readonly IGlobalSettings _globalSettings; + private readonly ISiteDomainHelper _siteDomainHelper; + + public DefaultUrlProvider(IRequestHandlerSection requestSettings, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) { _requestSettings = requestSettings; _logger = logger; - _globalSettings = globalSettings; + _globalSettings = globalSettings; + _siteDomainHelper = siteDomainHelper; } #region GetUrl @@ -37,13 +40,13 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it should return null. /// - public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string language = null) + public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, CultureInfo culture = null) { if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", "current"); // will not use cache if previewing - var route = umbracoContext.ContentCache.GetRouteById(id, language); + var route = umbracoContext.ContentCache.GetRouteById(id, culture); return GetUrlFromRoute(route, umbracoContext, id, current, mode); } @@ -57,7 +60,7 @@ namespace Umbraco.Web.Routing return null; } - var domainHelper = new DomainHelper(umbracoContext.PublishedShapshot.Domains); + var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); // extract domainUri and path // route is / or / @@ -98,7 +101,7 @@ namespace Umbraco.Web.Routing return null; } - var domainHelper = new DomainHelper(umbracoContext.PublishedShapshot.Domains); + var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); // extract domainUri and path // route is / or / diff --git a/src/Umbraco.Web/Routing/Domain.cs b/src/Umbraco.Web/Routing/Domain.cs index b9116c6b51..03048f0dd5 100644 --- a/src/Umbraco.Web/Routing/Domain.cs +++ b/src/Umbraco.Web/Routing/Domain.cs @@ -15,13 +15,14 @@ namespace Umbraco.Web.Routing /// The identifier of the content which supports the domain. /// The culture of the domain. /// A value indicating whether the domain is a wildcard domain. - public Domain(int id, string name, int contentId, CultureInfo culture, bool isWildcard) + public Domain(int id, string name, int contentId, CultureInfo culture, bool isWildcard, bool isDefault) { Id = id; Name = name; ContentId = contentId; Culture = culture; IsWildcard = isWildcard; + IsDefault = isDefault; } /// @@ -35,6 +36,7 @@ namespace Umbraco.Web.Routing ContentId = domain.ContentId; Culture = domain.Culture; IsWildcard = domain.IsWildcard; + IsDefault = domain.IsDefault; } /// @@ -61,5 +63,10 @@ namespace Umbraco.Web.Routing /// Gets a value indicating whether the domain is a wildcard domain. /// public bool IsWildcard { get; } + + /// + /// Gets a value indicating if this is the default domain for the website. + /// + public bool IsDefault { get; } } } diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index 79ccb9fbd4..d56473f530 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -12,11 +12,13 @@ namespace Umbraco.Web.Routing /// public class DomainHelper { - private readonly IDomainCache _domainCache; - - public DomainHelper(IDomainCache domainCache) + private readonly IDomainCache _domainCache; + private readonly ISiteDomainHelper _siteDomainHelper; + + public DomainHelper(IDomainCache domainCache, ISiteDomainHelper siteDomainHelper) { - _domainCache = domainCache; + _domainCache = domainCache; + _siteDomainHelper = siteDomainHelper; } #region Domain for Node @@ -43,8 +45,7 @@ namespace Umbraco.Web.Routing return null; // else filter - var helper = Current.SiteDomainHelper; - var domainAndUri = DomainForUri(domains, current, domainAndUris => helper.MapDomain(current, domainAndUris)); + var domainAndUri = DomainForUri(domains, current, domainAndUris => _siteDomainHelper.MapDomain(current, domainAndUris)); if (domainAndUri == null) throw new Exception("DomainForUri returned null."); @@ -88,8 +89,7 @@ namespace Umbraco.Web.Routing var domainAndUris = DomainsForUri(domains, current).ToArray(); // filter - var helper = Current.SiteDomainHelper; - return helper.MapDomains(current, domainAndUris, excludeDefault).ToArray(); + return _siteDomainHelper.MapDomains(current, domainAndUris, excludeDefault).ToArray(); } #endregion @@ -125,9 +125,10 @@ namespace Umbraco.Web.Routing DomainAndUri domainAndUri; if (current == null) - { - // take the first one by default (what else can we do?) - domainAndUri = domainsAndUris.First(); // .First() protected by .Any() above + { + //get the default domain (there should be one) + domainAndUri = domainsAndUris.FirstOrDefault(x => x.IsDefault); + if (domainAndUri == null) domainsAndUris.First(); // take the first one by default (what else can we do?) } else { diff --git a/src/Umbraco.Web/Routing/IUrlProvider.cs b/src/Umbraco.Web/Routing/IUrlProvider.cs index 3f6ee4dcc0..086f9c4767 100644 --- a/src/Umbraco.Web/Routing/IUrlProvider.cs +++ b/src/Umbraco.Web/Routing/IUrlProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using Umbraco.Web.PublishedCache; namespace Umbraco.Web.Routing @@ -21,7 +22,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it should return null. /// - string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string language = null); + string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, CultureInfo culture = null); /// /// Gets the other urls of a published content. diff --git a/src/Umbraco.Web/Routing/SiteDomainHelper.cs b/src/Umbraco.Web/Routing/SiteDomainHelper.cs index 1ceab6293a..deaa84b0e1 100644 --- a/src/Umbraco.Web/Routing/SiteDomainHelper.cs +++ b/src/Umbraco.Web/Routing/SiteDomainHelper.cs @@ -302,8 +302,13 @@ namespace Umbraco.Web.Routing throw new ArgumentException("Cannot be empty.", "domainAndUris"); // we do our best, but can't do the impossible - if (qualifiedSites == null) - return domainAndUris.First(); + if (qualifiedSites == null) + { + //fixme take the default + var defaultDomain = domainAndUris.FirstOrDefault(x => x.IsDefault); + if (defaultDomain == null) defaultDomain = domainAndUris.First(); //this shouldn't occur but just in case + return defaultDomain; + } // find a site that contains the current authority var currentSite = qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority)); diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 3375ac1be2..58f1689cb0 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -7,7 +7,8 @@ using Umbraco.Web.PublishedCache; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Web.Composing; - +using Umbraco.Core.Services; + namespace Umbraco.Web.Routing { /// @@ -23,13 +24,13 @@ namespace Umbraco.Web.Routing /// The Umbraco context. /// /// The list of url providers. - public UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders) + public UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders, IEntityService entityService) { if (routingSettings == null) throw new ArgumentNullException(nameof(routingSettings)); _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); - _urlProviders = urlProviders; - + _urlProviders = urlProviders; + _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); var provider = UrlProviderMode.Auto; Mode = provider; @@ -54,8 +55,9 @@ namespace Umbraco.Web.Routing } private readonly UmbracoContext _umbracoContext; - private readonly IEnumerable _urlProviders; - + private readonly IEnumerable _urlProviders; + private readonly IEntityService _entityService; + /// /// Gets or sets the provider url mode. /// @@ -76,7 +78,7 @@ namespace Umbraco.Web.Routing /// public string GetUrl(Guid id) { - var intId = Current.Services.EntityService.GetId(id, UmbracoObjectTypes.Document); + var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); return GetUrl(intId.Success ? intId.Result : -1); } @@ -93,7 +95,7 @@ namespace Umbraco.Web.Routing /// public string GetUrl(Guid id, bool absolute) { - var intId = Current.Services.EntityService.GetId(id, UmbracoObjectTypes.Document); + var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); return GetUrl(intId.Success ? intId.Result : -1, absolute); } @@ -111,7 +113,7 @@ namespace Umbraco.Web.Routing /// public string GetUrl(Guid id, Uri current, bool absolute) { - var intId = Current.Services.EntityService.GetId(id, UmbracoObjectTypes.Document); + var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); return GetUrl(intId.Success ? intId.Result : -1, current, absolute); } @@ -127,7 +129,7 @@ namespace Umbraco.Web.Routing /// public string GetUrl(Guid id, UrlProviderMode mode) { - var intId = Current.Services.EntityService.GetId(id, UmbracoObjectTypes.Document); + var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); return GetUrl(intId.Success ? intId.Result : -1, mode); } diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index 1d9a4a4236..a71bb51ddc 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -11,11 +11,6 @@ namespace Umbraco.Web.Routing { internal static class UrlProviderExtensions { - // fixme inject - private static ILocalizedTextService TextService => Current.Services.TextService; - private static IContentService ContentService => Current.Services.ContentService; - private static ILogger Logger => Current.Logger; - /// /// Gets the URLs for the content item /// @@ -26,28 +21,30 @@ namespace Umbraco.Web.Routing /// Use this when displaying URLs, if there are errors genertaing the urls the urls themselves will /// contain the errors. /// - public static IEnumerable GetContentUrls(this IContent content, UmbracoContext umbracoContext) + public static IEnumerable GetContentUrls(this IContent content, UrlProvider urlProvider, ILocalizedTextService textService, IContentService contentService, ILogger logger) { - if (content == null) throw new ArgumentNullException(nameof(content)); - if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); - + if (content == null) throw new ArgumentNullException(nameof(content)); + if (urlProvider == null) throw new ArgumentNullException(nameof(urlProvider)); + if (textService == null) throw new ArgumentNullException(nameof(textService)); + if (contentService == null) throw new ArgumentNullException(nameof(contentService)); + if (logger == null) throw new ArgumentNullException(nameof(logger)); + var urls = new List(); if (content.Published == false) { - urls.Add(TextService.Localize("content/itemNotPublished")); + urls.Add(textService.Localize("content/itemNotPublished")); return urls; } string url; - var urlProvider = umbracoContext.UrlProvider; try { url = urlProvider.GetUrl(content.Id); } catch (Exception e) { - Logger.Error("GetUrl exception.", e); + logger.Error("GetUrl exception.", e); url = "#ex"; } if (url == "#") @@ -57,17 +54,17 @@ namespace Umbraco.Web.Routing var parent = content; do { - parent = parent.ParentId > 0 ? parent.Parent(ContentService) : null; + parent = parent.ParentId > 0 ? parent.Parent(contentService) : null; } while (parent != null && parent.Published); urls.Add(parent == null - ? TextService.Localize("content/parentNotPublishedAnomaly") // oops - internal error - : TextService.Localize("content/parentNotPublished", new[] { parent.Name })); + ? textService.Localize("content/parentNotPublishedAnomaly") // oops - internal error + : textService.Localize("content/parentNotPublished", new[] { parent.Name })); } else if (url == "#ex") { - urls.Add(TextService.Localize("content/getUrlException")); + urls.Add(textService.Localize("content/getUrlException")); } else { @@ -81,7 +78,7 @@ namespace Umbraco.Web.Routing if (pcr.HasPublishedContent == false) { - urls.Add(TextService.Localize("content/routeError", new[] { "(error)" })); + urls.Add(textService.Localize("content/routeError", new[] { "(error)" })); } else if (pcr.PublishedContent.Id != content.Id) { @@ -103,7 +100,7 @@ namespace Umbraco.Web.Routing s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")"; } - urls.Add(TextService.Localize("content/routeError", s)); + urls.Add(textService.Localize("content/routeError", s)); } else { diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index d67bf50797..820d846d9f 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -169,9 +169,9 @@ namespace Umbraco.Web.Runtime // all built-in finders in the correct order, // devs can then modify this list on application startup .Append() - .Append() + .Append() .Append() - .Append() + .Append() .Append() .Append(); @@ -212,6 +212,7 @@ namespace Umbraco.Web.Runtime IUserService userService, IUmbracoSettingsSection umbracoSettings, IGlobalSettings globalSettings, + IEntityService entityService, UrlProviderCollection urlProviders) { // setup mvc and webapi services @@ -248,7 +249,8 @@ namespace Umbraco.Web.Runtime new WebSecurity(httpContext, userService, globalSettings), umbracoSettings, urlProviders, - globalSettings); + globalSettings, + entityService); // ensure WebAPI is initialized, after everything GlobalConfiguration.Configuration.EnsureInitialized(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 907eac0bde..67ce954045 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -394,6 +394,8 @@ + + @@ -1231,8 +1233,6 @@ - - diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index cb7a29f608..ef684a21ba 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -4,6 +4,7 @@ using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Services; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Runtime; @@ -17,7 +18,8 @@ namespace Umbraco.Web public class UmbracoContext : DisposableObject, IDisposeOnRequestEnd { private readonly IGlobalSettings _globalSettings; - private readonly Lazy _publishedSnapshot; + private readonly Lazy _publishedSnapshot; + private DomainHelper _domainHelper; private string _previewToken; private bool? _previewing; @@ -28,10 +30,10 @@ namespace Umbraco.Web /// /// /// An http context. - /// A published snapshot service. - /// A security helper. - /// The umbraco settings. - /// Some url providers. + /// A published snapshot service. + /// A security helper. + /// The umbraco settings. + /// Some url providers. /// /// A value indicating whether to replace the existing context. /// The "current" UmbracoContext. @@ -66,7 +68,8 @@ namespace Umbraco.Web WebSecurity webSecurity, IUmbracoSettingsSection umbracoSettings, IEnumerable urlProviders, - IGlobalSettings globalSettings, + IGlobalSettings globalSettings, + IEntityService entityService, bool replace = false) { if (umbracoContextAccessor == null) throw new ArgumentNullException(nameof(umbracoContextAccessor)); @@ -84,7 +87,7 @@ namespace Umbraco.Web // create & assign to accessor, dispose existing if any umbracoContextAccessor.UmbracoContext?.Dispose(); - return umbracoContextAccessor.UmbracoContext = new UmbracoContext(httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings); + return umbracoContextAccessor.UmbracoContext = new UmbracoContext(httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, entityService); } // initializes a new instance of the UmbracoContext class @@ -96,7 +99,8 @@ namespace Umbraco.Web WebSecurity webSecurity, IUmbracoSettingsSection umbracoSettings, IEnumerable urlProviders, - IGlobalSettings globalSettings) + IGlobalSettings globalSettings, + IEntityService entityService) { if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); if (publishedSnapshotService == null) throw new ArgumentNullException(nameof(publishedSnapshotService)); @@ -132,7 +136,7 @@ namespace Umbraco.Web // OriginalRequestUrl = GetRequestFromContext()?.Url ?? new Uri("http://localhost"); CleanedUmbracoUrl = UriUtility.UriToUmbraco(OriginalRequestUrl); - UrlProvider = new UrlProvider(this, umbracoSettings.WebRouting, urlProviders); + UrlProvider = new UrlProvider(this, umbracoSettings.WebRouting, urlProviders, entityService); } #endregion @@ -207,7 +211,25 @@ namespace Umbraco.Web /// /// Exposes the HttpContext for the current request /// - public HttpContextBase HttpContext { get; } + public HttpContextBase HttpContext { get; } + + /// + /// Creates and caches an instance of a DomainHelper + /// + /// + /// We keep creating new instances of DomainHelper, it would be better if we didn't have to do that so instead we can + /// have one attached to the UmbracoContext. This method accepts an external ISiteDomainHelper otherwise the UmbracoContext + /// ctor will have to have another parameter added only for this one method which is annoying and doesn't make a ton of sense + /// since the UmbracoContext itself doesn't use this. + /// + /// TODO The alternative is to have a IDomainHelperAccessor singleton which is cached per UmbracoContext + /// + internal DomainHelper GetDomainHelper(ISiteDomainHelper siteDomainHelper) + { + if (_domainHelper == null) + _domainHelper = new DomainHelper(PublishedShapshot.Domains, siteDomainHelper); + return _domainHelper; + } /// /// Gets a value indicating whether the request has debugging enabled diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 746c23a325..1578f5dc53 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -53,7 +53,10 @@ namespace Umbraco.Web public IPublishedSnapshotService PublishedSnapshotService { get; set; } [Inject] - public IUserService UserService { get; set; } + public IUserService UserService { get; set; } + + [Inject] + public IEntityService EntityService { get; set; } [Inject] public UrlProviderCollection UrlProviders { get; set; } @@ -111,7 +114,8 @@ namespace Umbraco.Web new WebSecurity(httpContext, UserService, GlobalSettings), UmbracoConfig.For.UmbracoSettings(), UrlProviders, - GlobalSettings, + GlobalSettings, + EntityService, true); } From 7c05f2e86cabecc10b3f8c009b716293bdb1bcb3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 14:39:52 +1000 Subject: [PATCH 23/97] fixes routing cache key --- src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs | 4 ++-- src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs index 9d887f4d40..e1b86b9ad6 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs @@ -62,9 +62,9 @@ namespace Umbraco.Web.PublishedCache.NuCache return "NuCache.ContentCache.RouteByContent[" + DraftOrPub(previewing) + id + LangId(culture) + "]"; } - public static string ContentCacheContentByRoute(string route, bool previewing) + public static string ContentCacheContentByRoute(string route, bool previewing, CultureInfo culture) { - return "NuCache.ContentCache.ContentByRoute[" + DraftOrPub(previewing) + route + "]"; + return "NuCache.ContentCache.ContentByRoute[" + DraftOrPub(previewing) + route + LangId(culture) + "]"; } //public static string ContentCacheRouteByContentStartsWith() diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 74598186f6..c6d380ca55 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -65,7 +65,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (route == null) throw new ArgumentNullException(nameof(route)); var cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing ? _elementsCache : _snapshotCache; - var key = CacheKeys.ContentCacheContentByRoute(route, preview); + var key = CacheKeys.ContentCacheContentByRoute(route, preview, culture); return cache.GetCacheItem(key, () => GetByRouteInternal(preview, route, hideTopLevelNode, culture)); } From de2784c28141222a2463c062b8c79e6c22212a5a Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 14:51:27 +1000 Subject: [PATCH 24/97] Adds culture optional parameters to all GetUrl methods on UrlProvider --- src/Umbraco.Web/Routing/UrlProvider.cs | 37 +++++++++++++------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 58f1689cb0..3fb0d6fd1d 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -8,6 +8,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Web.Composing; using Umbraco.Core.Services; +using System.Globalization; namespace Umbraco.Web.Routing { @@ -76,10 +77,10 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on Mode and on the current url. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(Guid id) + public string GetUrl(Guid id, CultureInfo culture = null) { var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); - return GetUrl(intId.Success ? intId.Result : -1); + return GetUrl(intId.Success ? intId.Result : -1, culture); } /// @@ -93,10 +94,10 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(Guid id, bool absolute) + public string GetUrl(Guid id, bool absolute, CultureInfo culture = null) { var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); - return GetUrl(intId.Success ? intId.Result : -1, absolute); + return GetUrl(intId.Success ? intId.Result : -1, absolute, culture); } /// @@ -111,10 +112,10 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(Guid id, Uri current, bool absolute) + public string GetUrl(Guid id, Uri current, bool absolute, CultureInfo culture = null) { var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); - return GetUrl(intId.Success ? intId.Result : -1, current, absolute); + return GetUrl(intId.Success ? intId.Result : -1, current, absolute, culture); } /// @@ -127,10 +128,10 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on the current url. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(Guid id, UrlProviderMode mode) + public string GetUrl(Guid id, UrlProviderMode mode, CultureInfo culture = null) { var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); - return GetUrl(intId.Success ? intId.Result : -1, mode); + return GetUrl(intId.Success ? intId.Result : -1, mode, culture); } /// @@ -142,9 +143,9 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on Mode and on the current url. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id) + public string GetUrl(int id, CultureInfo culture = null) { - return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, Mode); + return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, Mode, culture); } /// @@ -158,10 +159,10 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, bool absolute) + public string GetUrl(int id, bool absolute, CultureInfo culture = null) { var mode = absolute ? UrlProviderMode.Absolute : Mode; - return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode); + return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode, culture); } /// @@ -176,10 +177,10 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, Uri current, bool absolute) + public string GetUrl(int id, Uri current, bool absolute, CultureInfo culture = null) { var mode = absolute ? UrlProviderMode.Absolute : Mode; - return GetUrl(id, current, mode); + return GetUrl(id, current, mode, culture); } /// @@ -192,9 +193,9 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on the current url. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, UrlProviderMode mode) + public string GetUrl(int id, UrlProviderMode mode, CultureInfo culture = null) { - return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode); + return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode, culture); } /// @@ -208,9 +209,9 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, Uri current, UrlProviderMode mode) + public string GetUrl(int id, Uri current, UrlProviderMode mode, CultureInfo culture = null) { - var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, id, current, mode)) + var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, id, current, mode, culture)) .FirstOrDefault(u => u != null); return url ?? "#"; // legacy wants this } From d552d5dadbb00c2432a48cfa83c9993a73006849 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 15:27:33 +1000 Subject: [PATCH 25/97] Updates UI and mapping to update a property type to allow being culture variant --- .../services/umbdataformatter.service.js | 2 +- .../propertysettings/propertysettings.html | 208 +++++++++--------- .../Editors/ContentTypeController.cs | 13 +- .../ContentEditing/PropertyTypeBasic.cs | 3 + .../Mapping/ContentTypeMapperProfile.cs | 2 +- .../Mapping/PropertyTypeGroupResolver.cs | 3 +- .../Mapping/PropertyTypeVariationsResolver.cs | 23 ++ .../NuCache/DataSource/BTree.cs | 1 - src/Umbraco.Web/Umbraco.Web.csproj | 1 + 9 files changed, 150 insertions(+), 106 deletions(-) create mode 100644 src/Umbraco.Web/Models/Mapping/PropertyTypeVariationsResolver.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index dab6cb8eda..9608fe20fb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -56,7 +56,7 @@ }); var saveProperties = _.map(realProperties, function (p) { - var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile', 'isSensitiveData'); + var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile', 'isSensitiveData', 'allowCultureVariant'); return saveProperty; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html index 9df9c801b4..76be18a58f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html @@ -1,131 +1,137 @@
    -
    -
    -
    Required label
    -
    -
    - -
    -
    +
    +
    + +
    + -
    - -
    + - -
    +
    -
    +
    - + - + - -
    +
    +
    -
    +
    Property Type Variation
    -
    - - + - +
    - +
    -
    +
    - - + - - + + + + +
    + + + + + + diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 0aa43c8cfb..5ffe68afaa 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -169,7 +169,18 @@ namespace Umbraco.Web.Editors } public DocumentTypeDisplay PostSave(DocumentTypeSave contentTypeSave) - { + { + //Before we send this model into this saving/mapping pipeline, we need to do some cleanup on variations. + //If the doc type does not allow content variations, we need to update all of it's property types to not allow this either + //else we may end up with ysods. I'm unsure if the service level handles this but we'll make sure it is updated here + if (!contentTypeSave.AllowCultureVariant) + { + foreach(var prop in contentTypeSave.Groups.SelectMany(x => x.Properties)) + { + prop.AllowCultureVariant = false; + } + } + var savedCt = PerformPostSave( contentTypeSave: contentTypeSave, getContentType: i => Services.ContentTypeService.Get(i), diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs index b6f678068e..cde9d0dabc 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs @@ -46,5 +46,8 @@ namespace Umbraco.Web.Models.ContentEditing //SD: Is this really needed ? [DataMember(Name = "groupId")] public int GroupId { get; set; } + + [DataMember(Name = "allowCultureVariant")] + public bool AllowCultureVariant { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapperProfile.cs index bba27ce3f1..2ee9e38ff5 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapperProfile.cs @@ -163,7 +163,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.PropertyEditorAlias, opt => opt.Ignore()) .ForMember(dest => dest.DeleteDate, opt => opt.Ignore()) - .ForMember(dto => dto.Variations, opt => opt.Ignore()) // fixme - change when UI supports it! + .ForMember(dto => dto.Variations, opt => opt.ResolveUsing()) //only map if it is actually set .ForMember(dest => dest.Id, opt => opt.Condition(source => source.Id > 0)) diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs index 9a1aeda845..b9aa482b6a 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs @@ -220,7 +220,8 @@ namespace Umbraco.Web.Models.Mapping DataTypeId = p.DataTypeId, SortOrder = p.SortOrder, ContentTypeId = contentType.Id, - ContentTypeName = contentType.Name + ContentTypeName = contentType.Name, + AllowCultureVariant = p.Variations.HasFlag(Core.Models.ContentVariation.CultureNeutral) }); } diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeVariationsResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeVariationsResolver.cs new file mode 100644 index 0000000000..772caed964 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeVariationsResolver.cs @@ -0,0 +1,23 @@ +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; +using ContentVariation = Umbraco.Core.Models.ContentVariation; + +namespace Umbraco.Web.Models.Mapping +{ + internal class PropertyTypeVariationsResolver: IValueResolver + { + public ContentVariation Resolve(PropertyTypeBasic source, PropertyType destination, ContentVariation destMember, ResolutionContext context) + { + //this will always be the case, a content type will always be allowed to be invariant + var result = ContentVariation.InvariantNeutral; + + if (source.AllowCultureVariant) + { + result |= ContentVariation.CultureNeutral; + } + + return result; + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs index 29b252cd92..7329033405 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs @@ -147,7 +147,6 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource for (var i = 0; i < pcount; i++) { // read lang id - // fixme: This will need to change to string when stephane is done his culture work var key = PrimitiveSerializer.String.ReadFrom(stream); var val = new CultureVariation(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 67ce954045..65ca4c44b4 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -262,6 +262,7 @@ + From f49fa7b567348fc59029a0d2a2047064efe79035 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 15:41:50 +1000 Subject: [PATCH 26/97] don't show the variation toggle on a property type if the content type doesn't have it enabled, ensure the default variation is set on a new property type based on the content type setting --- .../directives/components/umbgroupsbuilder.directive.js | 1 + .../propertysettings/propertysettings.controller.js | 6 +++++- .../propertysettings/propertysettings.html | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js index c0be05addf..9ed180c23e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js @@ -496,6 +496,7 @@ scope.propertySettingsDialogModel.property = property; scope.propertySettingsDialogModel.contentType = scope.contentType; scope.propertySettingsDialogModel.contentTypeName = scope.model.name; + scope.propertySettingsDialogModel.contentTypeAllowCultureVariant = scope.model.allowCultureVariant; scope.propertySettingsDialogModel.view = "views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html"; scope.propertySettingsDialogModel.show = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.controller.js index 5cb2a218d5..18575985e1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.controller.js @@ -56,8 +56,12 @@ function activate() { - matchValidationType(); + //make the default the same as the content type + if (!$scope.model.property.id) { + $scope.model.property.allowCultureVariant = $scope.model.contentTypeAllowCultureVariant; + } + matchValidationType(); } function changeValidationPattern() { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html index 76be18a58f..ae47204f48 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html @@ -85,7 +85,7 @@ -
    +
    Property Type Variation
    From a1cd80a0f66ff600c90b8ef9af5d3e6d6d9210c8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 15:59:16 +1000 Subject: [PATCH 27/97] Removes GetLanguageByCultureCode since it is kind of useless and the name is also very misleading --- .../Repositories/ILanguageRepository.cs | 1 - .../Implement/LanguageRepository.cs | 9 +------ .../Services/ILocalizationService.cs | 9 +------ .../Services/Implement/LocalizationService.cs | 13 ---------- .../Repositories/DictionaryRepositoryTest.cs | 8 +++--- .../Repositories/LanguageRepositoryTest.cs | 25 ------------------- .../Services/LocalizationServiceTests.cs | 11 +------- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 2 +- 8 files changed, 8 insertions(+), 70 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs index 36dd10c3fb..a71128c44b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs @@ -4,7 +4,6 @@ namespace Umbraco.Core.Persistence.Repositories { public interface ILanguageRepository : IReadWriteQueryRepository { - ILanguage GetByCultureName(string cultureName); ILanguage GetByIsoCode(string isoCode); int GetIdByIsoCode(string isoCode); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index ae9336593d..e69b8f8f62 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -199,14 +199,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var entity = factory.BuildEntity(dto); return entity; } - - public ILanguage GetByCultureName(string cultureName) - { - // use the underlying GetMany which will force cache all languages - // TODO we are cloning ALL in GetMany just to retrieve ONE, this is surely not optimized - return GetMany().FirstOrDefault(x => x.CultureName.InvariantEquals(cultureName)); - } - + public ILanguage GetByIsoCode(string isoCode) { TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs index d61ee5e310..4f8406ebe4 100644 --- a/src/Umbraco.Core/Services/ILocalizationService.cs +++ b/src/Umbraco.Core/Services/ILocalizationService.cs @@ -102,14 +102,7 @@ namespace Umbraco.Core.Services /// Id of the /// ILanguage GetLanguageById(int id); - - /// - /// Gets a by its culture code - /// - /// Culture Code - also refered to as the Friendly name - /// - ILanguage GetLanguageByCultureCode(string cultureName); - + /// /// Gets a by its iso code /// diff --git a/src/Umbraco.Core/Services/Implement/LocalizationService.cs b/src/Umbraco.Core/Services/Implement/LocalizationService.cs index bf01605416..040996ca53 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizationService.cs @@ -290,19 +290,6 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a by its culture code - /// - /// Culture Name - also refered to as the Friendly name - /// - public ILanguage GetLanguageByCultureCode(string cultureName) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _languageRepository.GetByCultureName(cultureName); - } - } - /// /// Gets a by its iso code /// diff --git a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs index 357015a9e5..3a7840dbf5 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Persistence.Repositories { Translations = new List { - new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByCultureCode("en-US"), "Hello world") + new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"), "Hello world") } }; @@ -70,7 +70,7 @@ namespace Umbraco.Tests.Persistence.Repositories { Translations = new List { - new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByCultureCode("en-US"), "Hello world") + new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"), "Hello world") } }; @@ -100,7 +100,7 @@ namespace Umbraco.Tests.Persistence.Repositories { Translations = new List { - new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByCultureCode("en-US"), "Hello world") + new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"), "Hello world") } }; @@ -371,7 +371,7 @@ namespace Umbraco.Tests.Persistence.Repositories public void CreateTestData() { - var language = ServiceContext.LocalizationService.GetLanguageByCultureCode("en-US"); + var language = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); var languageDK = new Language("da-DK") { CultureName = "da-DK" }; ServiceContext.LocalizationService.Save(languageDK);//Id 2 diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index fbf43a1889..8ef6416185 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -76,31 +76,6 @@ namespace Umbraco.Tests.Persistence.Repositories } } - [Test] - public void Can_Perform_Get_By_Culture_Name_On_LanguageRepository() - { - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = provider.CreateScope()) - { - var repository = CreateRepository(provider); - - var au = CultureInfo.GetCultureInfo("en-AU"); - var language = (ILanguage)new Language(au.Name) - { - CultureName = au.DisplayName - }; - repository.Save(language); - - //re-get - language = repository.GetByCultureName(au.DisplayName); - - // Assert - Assert.That(language, Is.Not.Null); - Assert.That(language.HasIdentity, Is.True); - Assert.That(language.CultureName, Is.EqualTo(au.DisplayName)); - Assert.That(language.IsoCode, Is.EqualTo(au.Name)); - } - } [Test] public void Get_When_Id_Doesnt_Exist_Returns_Null() diff --git a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs index b2394200a1..c62f14a4b0 100644 --- a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs +++ b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs @@ -144,16 +144,7 @@ namespace Umbraco.Tests.Services //there's a call or two to get languages, so apart from that there should only be one call per level Assert.Less(scope.Database.AsUmbracoDatabase().SqlCount, 30); } - } - - [Test] - public void Can_Get_Language_By_Culture_Code() - { - var danish = ServiceContext.LocalizationService.GetLanguageByCultureCode("Danish"); - var english = ServiceContext.LocalizationService.GetLanguageByCultureCode("English"); - Assert.NotNull(danish); - Assert.NotNull(english); - } + } [Test] public void Can_GetLanguageById() diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 7c926f04f3..6fc04b6bcb 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -100,7 +100,7 @@ namespace Umbraco.Web.Routing else { var result = new List(); - var languageIds = new List(domainUris.Select(x => _localizationService.GetLanguageByCultureCode(x.Culture.Name)?.Id).Where(x => x.HasValue)); + var languageIds = new List(domainUris.Select(x => _localizationService.GetLanguageByIsoCode(x.Culture.Name)?.Id).Where(x => x.HasValue)); foreach (var langId in languageIds) { var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias, languageId: langId); From 43ca9593d4e610a02cf03aaf15a61c14caf5e347 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 16:29:55 +1000 Subject: [PATCH 28/97] fixes AliasUrlProvider --- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 39 ++++++------------- .../Runtime/WebRuntimeComponent.cs | 2 +- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 6fc04b6bcb..900af87931 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -70,9 +70,6 @@ namespace Umbraco.Web.Routing /// public IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) { - if (!FindByUrlAliasEnabled) - return Enumerable.Empty(); // we have nothing to say - var node = umbracoContext.ContentCache.GetById(id); if (node == null) return Enumerable.Empty(); @@ -100,19 +97,21 @@ namespace Umbraco.Web.Routing else { var result = new List(); - var languageIds = new List(domainUris.Select(x => _localizationService.GetLanguageByIsoCode(x.Culture.Name)?.Id).Where(x => x.HasValue)); - foreach (var langId in languageIds) + var languageIsoCodesToIds = _localizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x.Id); + foreach(var domainUri in domainUris) { - var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias, languageId: langId); - if (!string.IsNullOrWhiteSpace(umbracoUrlName)) + if (languageIsoCodesToIds.TryGetValue(domainUri.Culture.Name, out var langId)) { - var path = "/" + umbracoUrlName; - - result.AddRange(domainUris - .Select(domainUri => new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path))) - .Select(uri => UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString())); + var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias, languageId: langId); + if (!string.IsNullOrWhiteSpace(umbracoUrlName)) + { + var path = "/" + umbracoUrlName; + var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)); + result.Add(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString()); + } } } + return result; } } @@ -121,22 +120,6 @@ namespace Umbraco.Web.Routing #region Utilities - private static bool FindByUrlAliasEnabled - { - get - { - //TODO: The ContentFinderResolver instance should be injected in ctor with DI instead of using singleton! - - // finder - if (Current.ContentFinders.Any(x => x is ContentFinderByUrlAlias)) - return true; - - - // anything else, we can't detect - return false; - } - } - string CombinePaths(string path1, string path2) { string path = path1.TrimEnd('/') + path2; diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 820d846d9f..a0c482075a 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -159,7 +159,7 @@ namespace Umbraco.Web.Runtime .Append(); composition.Container.RegisterCollectionBuilder() - //.Append() // not enabled by default + .Append() .Append() .Append(); From 6db75d4226283e3925f06e30d9ad4f6ac2435139 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 17:11:09 +1000 Subject: [PATCH 29/97] Fixes PublishedContent.cs when the property index doesn't contain the alias index (due to adding a new property type and the json not matchin) --- src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index acfc5bdd31..b451b45a00 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -258,7 +258,12 @@ namespace Umbraco.Web.PublishedCache.NuCache public override IPublishedProperty GetProperty(string alias) { var index = _contentNode.ContentType.GetPropertyIndex(alias); - var property = index < 0 ? null : PropertiesArray[index]; + if (index < 0) return null; + //TODO: Should we log here? I think this can happen when property types are added/removed from the doc type and the json serialized properties + // no longer match the list of property types since that is how the PropertiesArray is populated. + //TODO: Does the PropertiesArray get repopulated on content save? + if (index > PropertiesArray.Length) return null; + var property = PropertiesArray[index]; return property; } From c5ca61f2490fd96b90f53c9073de8562ba193b21 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 17:15:35 +1000 Subject: [PATCH 30/97] Fixes PublishedContent.cs when the property index doesn't contain the alias index (due to adding a new property type and the json not matchin) --- src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index b451b45a00..a3120ecb68 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -261,8 +261,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (index < 0) return null; //TODO: Should we log here? I think this can happen when property types are added/removed from the doc type and the json serialized properties // no longer match the list of property types since that is how the PropertiesArray is populated. - //TODO: Does the PropertiesArray get repopulated on content save? - if (index > PropertiesArray.Length) return null; + if (index >= PropertiesArray.Length) return null; var property = PropertiesArray[index]; return property; } From 2e38f7b27b19490a94da745e81ad7cb21f633043 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Apr 2018 17:28:57 +1000 Subject: [PATCH 31/97] Fixes PublishedContent.cs when the property index doesn't contain the alias index (due to adding a new property type and the json not matchin) --- .../PublishedCache/NuCache/PublishedContent.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index a3120ecb68..4502245c5a 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -36,7 +36,14 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var propertyType in _contentNode.ContentType.PropertyTypes) { if (contentData.Properties.TryGetValue(propertyType.Alias, out var pdatas)) + { properties.Add(new Property(propertyType, this, pdatas, _publishedSnapshotAccessor)); + } + else + { + //it doesn't exist in our serialized json but we should add it as an empty property so they are in sync + properties.Add(new Property(propertyType, this, null, _publishedSnapshotAccessor)); + } } PropertiesArray = properties.ToArray(); } @@ -259,8 +266,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { var index = _contentNode.ContentType.GetPropertyIndex(alias); if (index < 0) return null; - //TODO: Should we log here? I think this can happen when property types are added/removed from the doc type and the json serialized properties - // no longer match the list of property types since that is how the PropertiesArray is populated. + //fixme: This should not happen since we align the PropertiesArray with the property types in the ctor, if this does happen maybe we should throw a descriptive exception if (index >= PropertiesArray.Length) return null; var property = PropertiesArray[index]; return property; From 23333710f5b761b6eb34bcc10b89c98a34a2dcb2 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 23 Apr 2018 12:53:17 +0200 Subject: [PATCH 32/97] Implement culture available/edited --- src/Umbraco.Core/EnumExtensions.cs | 22 + .../Install/DatabaseSchemaCreator.cs | 3 +- .../Migrations/Upgrade/UmbracoPlan.cs | 14 +- .../Upgrade/V_8_0_0/AddVariationTables1A.cs | 40 ++ ...riationTable.cs => AddVariationTables2.cs} | 8 +- src/Umbraco.Core/Models/Content.cs | 93 ++-- src/Umbraco.Core/Models/ContentBase.cs | 2 +- src/Umbraco.Core/Models/ContentTypeBase.cs | 2 +- src/Umbraco.Core/Models/IContent.cs | 6 + src/Umbraco.Core/Models/PropertyType.cs | 2 +- .../Persistence/Constants-DatabaseSchema.cs | 7 +- .../Dtos/ContentVersionCultureVariationDto.cs | 16 +- .../Dtos/DocumentCultureVariationDto.cs | 34 ++ .../Persistence/Factories/PropertyFactory.cs | 22 +- .../Implement/DocumentRepository.cs | 183 +++++-- .../Repositories/Implement/MediaRepository.cs | 4 +- .../Implement/MemberRepository.cs | 4 +- src/Umbraco.Core/Umbraco.Core.csproj | 5 +- src/Umbraco.Tests/Models/VariationTests.cs | 63 ++- .../Services/ContentServiceTests.cs | 500 +++++++++++------- .../Services/EntityServiceTests.cs | 8 +- src/Umbraco.Web/Editors/ContentController.cs | 3 +- .../Mapping/ContentItemDisplayNameResolver.cs | 10 +- .../Mapping/ContentTypeProfileExtensions.cs | 12 +- 24 files changed, 752 insertions(+), 311 deletions(-) create mode 100644 src/Umbraco.Core/EnumExtensions.cs create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs rename src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/{AddContentVariationTable.cs => AddVariationTables2.cs} (54%) create mode 100644 src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs diff --git a/src/Umbraco.Core/EnumExtensions.cs b/src/Umbraco.Core/EnumExtensions.cs new file mode 100644 index 0000000000..58e14bcadf --- /dev/null +++ b/src/Umbraco.Core/EnumExtensions.cs @@ -0,0 +1,22 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Core +{ + /// + /// Provides extension methods for various enumerations. + /// + public static class EnumExtensions + { + /// + /// Determines whether a variation has all flags set. + /// + public static bool Has(this ContentVariation variation, ContentVariation values) + => (variation & values) == values; + + /// + /// Determines whether a variation has at least a flag set. + /// + public static bool HasAny(this ContentVariation variation, ContentVariation values) + => (variation & values) != ContentVariation.Unknown; + } +} diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs index 6ce300845c..7d4058a0e4 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs @@ -82,7 +82,8 @@ namespace Umbraco.Core.Migrations.Install typeof (UserLoginDto), typeof (ConsentDto), typeof (AuditEntryDto), - typeof (ContentVersionCultureVariationDto) + typeof (ContentVersionCultureVariationDto), + typeof (DocumentCultureVariationDto) }; /// diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 8bec74173e..4954742908 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -116,10 +116,11 @@ namespace Umbraco.Core.Migrations.Upgrade Chain("{9DF05B77-11D1-475C-A00A-B656AF7E0908}"); Chain("{6FE3EF34-44A0-4992-B379-B40BC4EF1C4D}"); Chain("{7F59355A-0EC9-4438-8157-EB517E6D2727}"); - Chain("{66B6821A-0DE3-4DF8-A6A4-65ABD211EDDE}"); + Chain("{66B6821A-0DE3-4DF8-A6A4-65ABD211EDDE}"); + Chain("{49506BAE-CEBB-4431-A1A6-24AD6EBBBC57}"); // must chain to v8 final state (see at end of file) - Chain("{941B2ABA-2D06-4E04-81F5-74224F1DB037}"); + Chain("{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}"); // UPGRADE FROM 7, MORE RECENT @@ -201,12 +202,17 @@ namespace Umbraco.Core.Migrations.Upgrade Chain("{79591E91-01EA-43F7-AC58-7BD286DB1E77}"); // 8.0.0 - Chain("{941B2ABA-2D06-4E04-81F5-74224F1DB037}"); + // AddVariationTables1 has been superceeded by AddVariationTables2 + //Chain("{941B2ABA-2D06-4E04-81F5-74224F1DB037}"); + Chain("{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}"); + + // however, need to take care of ppl in post-AddVariationTables1 state + Add("{941B2ABA-2D06-4E04-81F5-74224F1DB037}", "{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}"); // FINAL STATE - MUST MATCH LAST ONE ABOVE ! // whenever this changes, update all references in this file! - Add(string.Empty, "{941B2ABA-2D06-4E04-81F5-74224F1DB037}"); + Add(string.Empty, "{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}"); } } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs new file mode 100644 index 0000000000..df77f34a2c --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs @@ -0,0 +1,40 @@ +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class AddVariationTables1A : MigrationBase + { + public AddVariationTables1A(IMigrationContext context) + : base(context) + { } + + // note - original AddVariationTables1 just did + // Create.Table().Do(); + // + // this is taking care of ppl left in this state + + public override void Migrate() + { + // note - original AddVariationTables1 just did + // Create.Table().Do(); + // + // it's been deprecated, not part of the main upgrade path, + // but we need to take care of ppl caught into the state + + // was not used + Delete.Column("available").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do(); + + // was not used + Delete.Column("availableDate").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do(); + AddColumn("date"); + + // name, languageId are now non-nullable + AlterColumn(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation, "name"); + AlterColumn(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation, "languageId"); + + Create.Table().Do(); + + // fixme - data migration? + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentVariationTable.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs similarity index 54% rename from src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentVariationTable.cs rename to src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs index efd0f33e99..5b6c913195 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentVariationTable.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs @@ -1,16 +1,18 @@ -using Umbraco.Core.Persistence.Dtos; +using System; +using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 { - public class AddContentVariationTable : MigrationBase + public class AddVariationTables2 : MigrationBase { - public AddContentVariationTable(IMigrationContext context) + public AddVariationTables2(IMigrationContext context) : base(context) { } public override void Migrate() { Create.Table().Do(); + Create.Table().Do(); // fixme - data migration? } diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index bb2192f187..22c409d8b1 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -19,8 +19,9 @@ namespace Umbraco.Core.Models private bool _published; private PublishedState _publishedState; private DateTime? _releaseDate; - private DateTime? _expireDate; - private Dictionary _publishNames; + private DateTime? _expireDate; + private Dictionary _publishInfos; + private HashSet _edited; private static readonly Lazy Ps = new Lazy(); @@ -199,37 +200,40 @@ namespace Umbraco.Core.Models [IgnoreDataMember] public string PublishName { get; internal set; } - /// - [IgnoreDataMember] - public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames; - - /// - public string GetPublishName(string culture) + // sets publish infos + // internal for repositories + // clear by clearing name + internal void SetPublishInfos(string culture, string name, DateTime date) { - if (culture == null) return PublishName; - if (_publishNames == null) return null; - return _publishNames.TryGetValue(culture, out var name) ? name : null; - } - - // sets a publish name - // internal for repositories - internal void SetPublishName(string culture, string name) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullOrEmptyException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentNullOrEmptyException(nameof(name)); if (culture == null) { PublishName = name; + PublishDate = date; return; } // private method, assume that culture is valid - if (_publishNames == null) - _publishNames = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (_publishInfos == null) + _publishInfos = new Dictionary(StringComparer.OrdinalIgnoreCase); - _publishNames[culture] = name; + _publishInfos[culture] = (name, date); + } + + /// + [IgnoreDataMember] + //public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames; + public IReadOnlyDictionary PublishNames => _publishInfos?.ToDictionary(x => x.Key, x => x.Value.Name) ?? NoNames; + + /// + public string GetPublishName(string culture) + { + if (culture == null) return PublishName; + if (_publishInfos == null) return null; + return _publishInfos.TryGetValue(culture, out var infos) ? infos.Name : null; } // clears a publish name @@ -241,23 +245,51 @@ namespace Umbraco.Core.Models return; } - if (_publishNames == null) return; - _publishNames.Remove(culture); - if (_publishNames.Count == 0) - _publishNames = null; + if (_publishInfos == null) return; + _publishInfos.Remove(culture); + if (_publishInfos.Count == 0) + _publishInfos = null; } // clears all publish names private void ClearPublishNames() { PublishName = null; - _publishNames = null; + _publishInfos = null; } /// public bool IsCulturePublished(string culture) => !string.IsNullOrWhiteSpace(GetPublishName(culture)); + /// + public DateTime GetDateCulturePublished(string culture) + { + if (_publishInfos != null && _publishInfos.TryGetValue(culture, out var infos)) + return infos.Date; + throw new InvalidOperationException($"Culture \"{culture}\" is not published."); + } + + /// + public bool IsCultureEdited(string culture) + { + return string.IsNullOrWhiteSpace(GetPublishName(culture)) || (_edited != null && _edited.Contains(culture)); + } + + // sets a publish edited + internal void SetCultureEdited(string culture) + { + if (_edited == null) + _edited = new HashSet(StringComparer.OrdinalIgnoreCase); + _edited.Add(culture); + } + + // sets all publish edited + internal void SetCultureEdited(IEnumerable cultures) + { + _edited = new HashSet(cultures, StringComparer.OrdinalIgnoreCase); + } + [IgnoreDataMember] public int PublishedVersionId { get; internal set; } @@ -276,11 +308,12 @@ namespace Umbraco.Core.Models if (string.IsNullOrWhiteSpace(Name)) throw new InvalidOperationException($"Cannot publish invariant culture without a name."); PublishName = Name; + var now = DateTime.Now; foreach (var (culture, name) in Names) { if (string.IsNullOrWhiteSpace(name)) throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name."); - SetPublishName(culture, name); + SetPublishInfos(culture, name, now); } // property.PublishAllValues only deals with supported variations (if any) @@ -308,7 +341,7 @@ namespace Umbraco.Core.Models var name = GetName(culture); if (string.IsNullOrWhiteSpace(name)) throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name."); - SetPublishName(culture, name); + SetPublishInfos(culture, name, DateTime.Now); } // property.PublishValue throws on invalid variation, so filter them out @@ -331,7 +364,7 @@ namespace Umbraco.Core.Models var name = GetName(culture); if (string.IsNullOrWhiteSpace(name)) throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name."); - SetPublishName(culture, name); + SetPublishInfos(culture, name, DateTime.Now); // property.PublishCultureValues only deals with supported variations (if any) foreach (var property in Properties) diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index ca5f9024a7..7bc327c2d0 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -164,7 +164,7 @@ namespace Umbraco.Core.Models return; } - if ((ContentTypeBase.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) == 0) + if (!ContentTypeBase.Variations.HasAny(ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) throw new NotSupportedException("Content type does not support varying name by culture."); if (_names == null) diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index 7d0ffea31e..952fa0cd88 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -221,7 +221,7 @@ namespace Umbraco.Core.Models { variation = ContentVariation.InvariantNeutral; } - if ((Variations & variation) == 0) + if (!Variations.Has(variation)) { if (throwIfInvalid) throw new NotSupportedException($"Variation {variation} is invalid for content type \"{Alias}\"."); diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 69e01f4963..ab995b9b04 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -89,6 +89,12 @@ namespace Umbraco.Core.Models /// whenever values for this culture are unpublished. /// bool IsCulturePublished(string culture); + + // fixme doc + DateTime GetDateCulturePublished(string culture); + + // fixme doc + bool IsCultureEdited(string culture); /// /// Gets the name of the published version of the content for a given culture. diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index b34eca7c57..66299e7749 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -243,7 +243,7 @@ namespace Umbraco.Core.Models { variation = ContentVariation.InvariantNeutral; } - if ((Variations & variation) == 0) + if (!Variations.Has(variation)) { if (throwIfInvalid) throw new NotSupportedException($"Variation {variation} is invalid for property type \"{Alias}\"."); diff --git a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs index 68afd4f244..79dd45b73b 100644 --- a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs +++ b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs @@ -17,10 +17,10 @@ namespace Umbraco.Core public const string NodeXml = /*TableNamePrefix*/ "cms" + "ContentXml"; public const string NodePreviewXml = /*TableNamePrefix*/ "cms" + "PreviewXml"; // fixme dbfix kill merge with ContentXml - public const string ContentType = /*TableNamePrefix*/ "cms" + "ContentType"; // fixme dbfixrename and split uElementType, uDocumentType + public const string ContentType = /*TableNamePrefix*/ "cms" + "ContentType"; // fixme dbfix rename and split uElementType, uDocumentType public const string ContentChildType = /*TableNamePrefix*/ "cms" + "ContentTypeAllowedContentType"; - public const string DocumentType = /*TableNamePrefix*/ "cms" + "DocumentType"; // fixme dbfixmust rename corresponding DTO - public const string ElementTypeTree = /*TableNamePrefix*/ "cms" + "ContentType2ContentType"; // fixme dbfixwhy can't we just use uNode for this? + public const string DocumentType = /*TableNamePrefix*/ "cms" + "DocumentType"; // fixme dbfix must rename corresponding DTO + public const string ElementTypeTree = /*TableNamePrefix*/ "cms" + "ContentType2ContentType"; // fixme dbfix why can't we just use uNode for this? public const string DataType = TableNamePrefix + "DataType"; public const string Template = /*TableNamePrefix*/ "cms" + "Template"; @@ -28,6 +28,7 @@ namespace Umbraco.Core public const string ContentVersion = TableNamePrefix + "ContentVersion"; public const string ContentVersionCultureVariation = TableNamePrefix + "ContentVersionCultureVariation"; public const string Document = TableNamePrefix + "Document"; + public const string DocumentCultureVariation = TableNamePrefix + "DocumentCultureVariation"; public const string DocumentVersion = TableNamePrefix + "DocumentVersion"; public const string MediaVersion = TableNamePrefix + "MediaVersion"; diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs index 104a707669..dbba897f7a 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs @@ -25,21 +25,21 @@ namespace Umbraco.Core.Persistence.Dtos [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_LanguageId")] public int LanguageId { get; set; } + // this is convenient to carry the culture around, but has no db counterpart + [Ignore] + public string Culture { get; set; } + [Column("name")] - [NullSetting(NullSetting = NullSettings.Null)] public string Name { get; set; } - [Column("available")] - public bool Available { get; set; } - - [Column("availableDate")] - [NullSetting(NullSetting = NullSettings.Null)] - public DateTime? AvailableDate { get; set; } + [Column("date")] + public DateTime Date { get; set; } + // fixme want? [Column("availableUserId")] // [ForeignKey(typeof(UserDto))] -- there is no foreign key so we can delete users without deleting associated content //[NullSetting(NullSetting = NullSettings.Null)] - public int AvailableUserId { get; set; } + public int PublishedUserId { get; set; } [Column("edited")] public bool Edited { get; set; } diff --git a/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs b/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs new file mode 100644 index 0000000000..be4cf7c023 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs @@ -0,0 +1,34 @@ +using NPoco; +using Umbraco.Core.Persistence.DatabaseAnnotations; + +namespace Umbraco.Core.Persistence.Dtos +{ + [TableName(TableName)] + [PrimaryKey("id")] + [ExplicitColumns] + internal class DocumentCultureVariationDto + { + private const string TableName = Constants.DatabaseSchema.Tables.DocumentCultureVariation; + + [Column("id")] + [PrimaryKeyColumn] + public int Id { get; set; } + + [Column("nodeId")] + [ForeignKey(typeof(NodeDto))] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId,languageId")] + public int NodeId { get; set; } + + [Column("languageId")] + [ForeignKey(typeof(LanguageDto))] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_LanguageId")] + public int LanguageId { get; set; } + + // this is convenient to carry the culture around, but has no db counterpart + [Ignore] + public string Culture { get; set; } + + [Column("edited")] + public bool Edited { get; set; } + } +} diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index 10f2918204..d25f921f1a 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -4,7 +4,6 @@ using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Services; namespace Umbraco.Core.Persistence.Factories { @@ -90,15 +89,25 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - public static IEnumerable BuildDtos(int currentVersionId, int publishedVersionId, IEnumerable properties, ILanguageRepository languageRepository, out bool edited) + public static IEnumerable BuildDtos(int currentVersionId, int publishedVersionId, IEnumerable properties, ILanguageRepository languageRepository, out bool edited, out HashSet editedCultures) { var propertyDataDtos = new List(); edited = false; + editedCultures = null; // don't allocate unless necessary foreach (var property in properties) { if (property.PropertyType.IsPublishing) - { + { + // fixme + // why only CultureNeutral? + // then, the tree can only show when a CultureNeutral value has been modified, but not when + // a CultureSegment has been modified, so if I edit some french/mobile thing, the tree will + // NOT tell me that I have changes? + + var editingCultures = property.PropertyType.Variations.Has(ContentVariation.CultureNeutral); + if (editingCultures && editedCultures == null) editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase); + // publishing = deal with edit and published values foreach (var propertyValue in property.Values) { @@ -117,6 +126,13 @@ namespace Umbraco.Core.Persistence.Factories // use explicit equals here, else object comparison fails at comparing eg strings var sameValues = propertyValue.PublishedValue == null ? propertyValue.EditedValue == null : propertyValue.PublishedValue.Equals(propertyValue.EditedValue); edited |= !sameValues; + + if (editingCultures && // cultures can be edited, ie CultureNeutral is supported + propertyValue.Culture != null && propertyValue.Segment == null && // and value is CultureNeutral + !sameValues) // and edited and published are different + { + editedCultures.Add(propertyValue.Culture); // report culture as edited + } } } else diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index 848019cf95..e47e8aa33e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -315,10 +315,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, content.PublishedVersionId, entity.Properties, LanguageRepository, out var edited); + var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, content.PublishedVersionId, entity.Properties, LanguageRepository, out var edited, out var editedCultures); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); + // name also impacts 'edited' + if (content.PublishName != content.Name) + edited = true; + // persist the document dto // at that point, when publishing, the entity still has its old Published value // so we need to explicitely update the dto to persist the correct value @@ -329,12 +333,24 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(dto); // persist the variations - if ((content.ContentType.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) > 0) + if (content.ContentType.Variations.HasAny(Models.ContentVariation.CultureNeutral | Models.ContentVariation.CultureSegment)) { - foreach (var variationDto in GetVariationDtos(content, publishing)) - Database.Insert(variationDto); + // names also impact 'edited' + foreach (var (culture, name) in content.Names) + if (name != content.GetPublishName(culture)) + (editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(culture); + + // insert content variations + Database.InsertBulk(GetContentVariationDtos(content, publishing)); + + // insert document variations + Database.InsertBulk(GetDocumentVariationDtos(content, publishing, editedCultures)); } + // refresh content + if (editedCultures != null) + content.SetCultureEdited(editedCultures); + // trigger here, before we reset Published etc OnUowRefreshedEntity(new ScopedEntityEventArgs(AmbientScope, entity)); @@ -454,26 +470,51 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(documentVersionDto); } - var versionToDelete = publishing ? new[] { content.VersionId, content.PublishedVersionId } : new[] { content.VersionId }; - - // replace the property data - var deletePropertyDataSql = Sql().Delete().WhereIn(x => x.VersionId, versionToDelete); + // replace the property data (rather than updating) + // only need to delete for the version that existed, the new version (if any) has no property data yet + var versionToDelete = publishing ? content.PublishedVersionId : content.VersionId; + var deletePropertyDataSql = Sql().Delete().Where(x => x.VersionId == versionToDelete); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, publishing ? content.PublishedVersionId : 0, entity.Properties, LanguageRepository, out var edited); + // insert property data + var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, publishing ? content.PublishedVersionId : 0, entity.Properties, LanguageRepository, out var edited, out var editedCultures); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); - // replace the variations - if ((content.ContentType.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) > 0) - { - var deleteVariations = Sql().Delete().WhereIn(x => x.VersionId, versionToDelete); - Database.Execute(deleteVariations); + // name also impacts 'edited' + if (content.PublishName != content.Name) + edited = true; - foreach (var variationDto in GetVariationDtos(content, publishing)) - Database.Insert(variationDto); + if (content.ContentType.Variations.HasAny(Models.ContentVariation.CultureNeutral | Models.ContentVariation.CultureSegment)) + { + // names also impact 'edited' + foreach (var (culture, name) in content.Names) + if (name != content.GetPublishName(culture)) + (editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(culture); + + // replace the content version variations (rather than updating) + // only need to delete for the version that existed, the new version (if any) has no property data yet + var deleteContentVariations = Sql().Delete().Where(x => x.VersionId == versionToDelete); + Database.Execute(deleteContentVariations); + + // replace the document version variations (rather than updating) + var deleteDocumentVariations = Sql().Delete().Where(x => x.NodeId == content.Id); + Database.Execute(deleteDocumentVariations); + + // fixme is it OK to use NPoco InsertBulk here, or should we use our own BulkInsertRecords? + // (same in PersistNewItem above) + + // insert content variations + Database.InsertBulk(GetContentVariationDtos(content, publishing)); + + // insert document variations + Database.InsertBulk(GetDocumentVariationDtos(content, publishing, editedCultures)); } + // refresh content + if (editedCultures != null) + content.SetCultureEdited(editedCultures); + // update the document dto // at that point, when un/publishing, the entity still has its old Published value // so we need to explicitely update the dto to persist the correct value @@ -853,12 +894,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } // set variations, if varying - temps = temps.Where(x => x.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral | ContentVariation.CultureSegment)).ToList(); + temps = temps.Where(x => x.ContentType.Variations.HasAny(Models.ContentVariation.CultureNeutral | Models.ContentVariation.CultureSegment)).ToList(); if (temps.Count > 0) { - var variations = GetVariations(temps); + // load all variations for all documents from database, in one query + var contentVariations = GetContentVariations(temps); + var documentVariations = GetDocumentVariations(temps); foreach (var temp in temps) - SetContentVariations(temp.Content, variations); + SetVariations(temp.Content, contentVariations, documentVariations); } return content; @@ -886,10 +929,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement content.Properties = properties[dto.DocumentVersionDto.Id]; // set variations, if varying - if ((contentType.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) > 0) + if (contentType.Variations.HasAny(Models.ContentVariation.CultureNeutral | Models.ContentVariation.CultureSegment)) { - var variations = GetVariations(ltemp); - SetContentVariations(content, variations); + var contentVariations = GetContentVariations(ltemp); + var documentVariations = GetDocumentVariations(ltemp); + SetVariations(content, contentVariations, documentVariations); } // reset dirty initial properties (U4-1946) @@ -897,21 +941,20 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return content; } - private void SetContentVariations(Content content, IDictionary> variations) + private void SetVariations(Content content, IDictionary> contentVariations, IDictionary> documentVariations) { - if (variations.TryGetValue(content.VersionId, out var variation)) - foreach (var v in variation) - { + if (contentVariations.TryGetValue(content.VersionId, out var contentVariation)) + foreach (var v in contentVariation) content.SetName(v.Culture, v.Name); - } - if (content.PublishedVersionId > 0 && variations.TryGetValue(content.PublishedVersionId, out variation)) - foreach (var v in variation) - { - content.SetPublishName(v.Culture, v.Name); - } + if (content.PublishedVersionId > 0 && contentVariations.TryGetValue(content.PublishedVersionId, out contentVariation)) + foreach (var v in contentVariation) + content.SetPublishInfos(v.Culture, v.Name, v.Date); + if (documentVariations.TryGetValue(content.Id, out var documentVariation)) + foreach (var v in documentVariation.Where(x => x.Edited)) + content.SetCultureEdited(v.Culture); } - private IDictionary> GetVariations(List> temps) + private IDictionary> GetContentVariations(List> temps) where T : class, IContentBase { var versions = new List(); @@ -921,7 +964,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (temp.PublishedVersionId > 0) versions.Add(temp.PublishedVersionId); } - if (versions.Count == 0) return new Dictionary>(); + if (versions.Count == 0) return new Dictionary>(); var dtos = Database.FetchByGroups(versions, 2000, batch => Sql() @@ -929,50 +972,104 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .From() .WhereIn(x => x.VersionId, batch)); - var variations = new Dictionary>(); + var variations = new Dictionary>(); foreach (var dto in dtos) { if (!variations.TryGetValue(dto.VersionId, out var variation)) - variations[dto.VersionId] = variation = new List(); + variations[dto.VersionId] = variation = new List(); - variation.Add(new CultureVariation + variation.Add(new ContentVariation { Culture = LanguageRepository.GetIsoCodeById(dto.LanguageId), Name = dto.Name, - Available = dto.Available + Date = dto.Date }); } return variations; } - private IEnumerable GetVariationDtos(IContent content, bool publishing) + private IDictionary> GetDocumentVariations(List> temps) + where T : class, IContentBase { + var ids = temps.Select(x => x.Id); + + var dtos = Database.FetchByGroups(ids, 2000, batch => + Sql() + .Select() + .From() + .WhereIn(x => x.NodeId, batch)); + + var variations = new Dictionary>(); + + foreach (var dto in dtos) + { + if (!variations.TryGetValue(dto.NodeId, out var variation)) + variations[dto.NodeId] = variation = new List(); + + variation.Add(new DocumentVariation + { + Culture = LanguageRepository.GetIsoCodeById(dto.LanguageId), + Edited = dto.Edited + }); + } + + return variations; + } + + private IEnumerable GetContentVariationDtos(IContent content, bool publishing) + { + // create dtos for the 'current' (non-published) version, all cultures foreach (var (culture, name) in content.Names) yield return new ContentVersionCultureVariationDto { VersionId = content.VersionId, LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), - Name = name + Culture = culture, + Name = name, + Date = content.UpdateDate }; + // if not publishing, we're just updating the 'current' (non-published) version, + // so there are no DTOs to create for the 'published' version which remains unchanged if (!publishing) yield break; + // create dtos for the 'published' version, for published cultures (those having a name) foreach (var (culture, name) in content.PublishNames) yield return new ContentVersionCultureVariationDto { VersionId = content.PublishedVersionId, LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), - Name = name + Culture = culture, + Name = name, + Date = content.GetDateCulturePublished(culture) }; } - private class CultureVariation + private IEnumerable GetDocumentVariationDtos(IContent content, bool publishing, HashSet editedCultures) + { + foreach (var (culture, name) in content.Names) + yield return new DocumentCultureVariationDto + { + NodeId = content.Id, + LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), + Culture = culture, + Edited = !content.IsCulturePublished(culture) || editedCultures.Contains(culture) // if not published, always edited + }; + } + + private class ContentVariation { public string Culture { get; set; } public string Name { get; set; } - public bool Available { get; set; } + public DateTime Date { get; set; } + } + + private class DocumentVariation + { + public string Culture { get; set; } + public bool Edited { get; set; } } #region Utilities diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs index dfb30a9cb3..982a5bb885 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs @@ -279,7 +279,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(mediaVersionDto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -336,7 +336,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // replace the property data var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == media.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs index 04e5d64b06..98c38603b1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs @@ -306,7 +306,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(dto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -371,7 +371,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // replace the property data var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == member.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 7549d4ba2b..12631dc8fe 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -320,6 +320,7 @@ + @@ -336,7 +337,8 @@ - + + @@ -369,6 +371,7 @@ + diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 70e3d574b3..5bfd3f2de5 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -164,22 +164,26 @@ namespace Umbraco.Tests.Models const string langFr = "fr-FR"; const string langUk = "en-UK"; + // throws if the content type does not support the variation Assert.Throws(() => content.SetName(langFr, "name-fr")); + // now it will work contentType.Variations = ContentVariation.CultureNeutral; + // invariant name works content.Name = "name"; Assert.AreEqual("name", content.GetName(null)); content.SetName(null, "name2"); Assert.AreEqual("name2", content.Name); Assert.AreEqual("name2", content.GetName(null)); + // variant names work content.SetName(langFr, "name-fr"); content.SetName(langUk, "name-uk"); - Assert.AreEqual("name-fr", content.GetName(langFr)); Assert.AreEqual("name-uk", content.GetName(langUk)); + // variant dictionary of names work Assert.AreEqual(2, content.Names.Count); Assert.IsTrue(content.Names.ContainsKey(langFr)); Assert.AreEqual("name-fr", content.Names[langFr]); @@ -188,7 +192,7 @@ namespace Umbraco.Tests.Models } [Test] - public void ContentTests() + public void ContentPublishValues() { const string langFr = "fr-FR"; @@ -296,6 +300,61 @@ namespace Umbraco.Tests.Models Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); } + [Test] + public void ContentPublishVariations() + { + const string langFr = "fr-FR"; + const string langUk = "en-UK"; + const string langEs = "es-ES"; + + var propertyType = new PropertyType("editor", ValueStorageType.Nvarchar) { Alias = "prop" }; + var contentType = new ContentType(-1) { Alias = "contentType" }; + contentType.AddPropertyType(propertyType); + + var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; + + contentType.Variations |= ContentVariation.CultureNeutral; + propertyType.Variations |= ContentVariation.CultureNeutral; + + content.SetValue("prop", "a"); + content.SetValue("prop", "a-fr", langFr); + content.SetValue("prop", "a-uk", langUk); + content.SetValue("prop", "a-es", langEs); + + // cannot publish without a name + Assert.Throws(() => content.PublishValues(langFr)); + + // works with a name + // and then FR is available, and published + content.SetName(langFr, "name-fr"); + content.PublishValues(langFr); + + // now UK is available too + content.SetName(langUk, "name-uk"); + + // test available, published + Assert.IsTrue(content.IsCultureAvailable(langFr)); + Assert.IsTrue(content.IsCulturePublished(langFr)); + Assert.AreEqual("name-fr", content.GetPublishName(langFr)); + Assert.AreNotEqual(DateTime.MinValue, content.GetDateCulturePublished(langFr)); + Assert.IsFalse(content.IsCultureEdited(langFr)); // once published, edited is *wrong* until saved + + Assert.IsTrue(content.IsCultureAvailable(langUk)); + Assert.IsFalse(content.IsCulturePublished(langUk)); + Assert.IsNull(content.GetPublishName(langUk)); + Assert.Throws(() => content.GetDateCulturePublished(langUk)); // not published! + Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited + + Assert.IsFalse(content.IsCultureAvailable(langEs)); + Assert.IsFalse(content.IsCulturePublished(langEs)); + Assert.IsNull(content.GetPublishName(langEs)); + Assert.Throws(() => content.GetDateCulturePublished(langEs)); // not published! + Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited + + // cannot test IsCultureEdited here - as that requires the content service and repository + // see: ContentServiceTests.Can_SaveRead_Variations + } + [Test] public void IsDirtyTests() { diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index b3bdf4f1ba..c45d9be5f3 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2475,231 +2475,351 @@ namespace Umbraco.Tests.Services } [Test] - public void Can_SaveAndRead_Names() + public void Can_SaveRead_Variations() { var languageService = ServiceContext.LocalizationService; var langFr = new Language("fr-FR"); var langUk = new Language("en-UK"); - var langDe = new Language("de-DE"); + var langDe = new Language("de-DE"); + languageService.Save(langFr); languageService.Save(langUk); - languageService.Save(langDe); + languageService.Save(langDe); var contentTypeService = ServiceContext.ContentTypeService; - - // fixme - // contentType.Variations is InvariantNeutral | CultureNeutral - // propertyType.Variations can only be a subset of contentType.Variations - ie cannot *add* anything - // (at least, we should validate this) - // but then, - // if the contentType supports InvariantNeutral | CultureNeutral, - // the propertyType should support InvariantNeutral, or both, but not solely CultureNeutral? - // but does this mean that CultureNeutral implies InvariantNeutral? - // can a contentType *not* support InvariantNeutral? - - var contentType = contentTypeService.Get("umbTextpage"); + + // fixme + // contentType.Variations is InvariantNeutral | CultureNeutral + // propertyType.Variations can only be a subset of contentType.Variations - ie cannot *add* anything + // (at least, we should validate this) + // but then, + // if the contentType supports InvariantNeutral | CultureNeutral, + // the propertyType should support InvariantNeutral, or both, but not solely CultureNeutral? + // but does this mean that CultureNeutral implies InvariantNeutral? + // can a contentType *not* support InvariantNeutral? + + var contentType = contentTypeService.Get("umbTextpage"); contentType.Variations = ContentVariation.InvariantNeutral | ContentVariation.CultureNeutral; contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Nvarchar, "prop") { Variations = ContentVariation.CultureNeutral }); - contentTypeService.Save(contentType); - + contentTypeService.Save(contentType); + var contentService = ServiceContext.ContentService; - var content = contentService.Create("Home US", - 1, "umbTextpage"); + var content = contentService.Create("Home US", - 1, "umbTextpage"); + + // act + + content.SetValue("author", "Barack Obama"); + content.SetValue("prop", "value-fr1", langFr.IsoCode); + content.SetValue("prop", "value-uk1", langUk.IsoCode); + content.SetName(langFr.IsoCode, "name-fr"); + content.SetName(langUk.IsoCode, "name-uk"); + contentService.Save(content); + + // content has been saved, + // it has names, but no publishNames, and no published cultures + + var content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US", content2.Name); + Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode)); + + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.IsNull(content2.GetValue("prop", langUk.IsoCode, published: true)); + + Assert.IsNull(content2.PublishName); + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.IsNull(content2.GetPublishName(langUk.IsoCode)); + + // only fr and uk have a name, and are available + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // nothing has been published yet + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false)); + + // not published => must be edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + + // act + + content.PublishValues(langFr.IsoCode); + content.PublishValues(langUk.IsoCode); + contentService.SaveAndPublish(content); + + // both FR and UK have been published, + // and content has been published, + // it has names, publishNames, and published cultures + + content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US", content2.Name); + Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode)); + + Assert.IsNull(content2.PublishName); // we haven't published InvariantNeutral + Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); + + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode)); + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // fr and uk have been published now + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); + + // fr and uk, published without changes, not edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true)); - // act - - content.SetValue("author", "Barack Obama"); - content.SetValue("prop", "value-fr1", langFr.IsoCode); - content.SetValue("prop", "value-uk1", langUk.IsoCode); - content.SetName(langFr.IsoCode, "name-fr"); - content.SetName(langUk.IsoCode, "name-uk"); - contentService.Save(content); + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw - // content has been saved, - // it has names, but no publishNames, and no published cultures + // note that content and content2 culture published dates might be slightly different due to roundtrip to database - var content2 = contentService.GetById(content.Id); + + // act + + content.PublishValues(); + contentService.SaveAndPublish(content); + + // now it has publish name for invariant neutral + + content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US", content2.PublishName); - Assert.AreEqual("Home US", content2.Name); - Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode)); - Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode)); + + // act + + content.SetName(null, "Home US2"); + content.SetName(langFr.IsoCode, "name-fr2"); + content.SetName(langUk.IsoCode, "name-uk2"); + content.SetValue("author", "Barack Obama2"); + content.SetValue("prop", "value-fr2", langFr.IsoCode); + content.SetValue("prop", "value-uk2", langUk.IsoCode); + contentService.Save(content); + + // content has been saved, + // it has updated names, unchanged publishNames, and published cultures + + content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); + + Assert.AreEqual("Home US", content2.PublishName); + Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); + + Assert.AreEqual("Barack Obama2", content2.GetValue("author")); + Assert.AreEqual("Barack Obama", content2.GetValue("author", published: true)); + + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode)); - Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); - Assert.IsNull(content2.GetValue("prop", langUk.IsoCode, published: true)); + // we have changed values so now fr and uk are edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - Assert.IsNull(content2.PublishName); - Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); - Assert.IsNull(content2.GetPublishName(langUk.IsoCode)); + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw - Assert.IsTrue(content.IsCultureAvailable(langFr.IsoCode)); - Assert.IsTrue(content.IsCultureAvailable(langUk.IsoCode)); - Assert.IsFalse(content.IsCultureAvailable(langDe.IsoCode)); + + // act + // cannot just 'save' since we are changing what's published! + + content.ClearPublishedValues(langFr.IsoCode); + contentService.SaveAndPublish(content); + + // content has been published, + // the french culture is gone + + content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); + + Assert.AreEqual("Home US", content2.PublishName); + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); + + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); + + Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // fr is not published anymore + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + + // and so, fr has to be edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); - Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode)); - // act + // act - content.PublishValues(langFr.IsoCode); - content.PublishValues(langUk.IsoCode); - contentService.SaveAndPublish(content); + contentService.Unpublish(content); + + // content has been unpublished, + // but properties, names, etc. retain their 'published' values so the content + // can be re-published in its exact original state (before being unpublished) + // + // BEWARE! + // in order for a content to be unpublished as a whole, and then republished in + // its exact previous state, properties and names etc. retain their published + // values even though the content is not published - hence many things being + // non-null or true below - always check against content.Published to be sure + + content2 = contentService.GetById(content.Id); + + Assert.IsFalse(content2.Published); + + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); + + Assert.AreEqual("Home US", content2.PublishName); // not null, see note above + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); // not null, see note above + + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); // has value, see note above + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // fr is not published anymore - uk still is, see note above + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + + // and so, fr has to be edited - uk still is + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - // both FR and UK have been published, - // and content has been published, - // it has names, publishNames, and published cultures - content2 = contentService.GetById(content.Id); + // act - Assert.AreEqual("Home US", content2.Name); - Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode)); - Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode)); + contentService.SaveAndPublish(content); - Assert.IsNull(content2.PublishName); // we haven't published InvariantNeutral - Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); + // content has been re-published, + // everything is back to what it was before being unpublished - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); + content2 = contentService.GetById(content.Id); + + Assert.IsTrue(content2.Published); + + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); + + Assert.AreEqual("Home US", content2.PublishName); + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); + + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // no change, back to published + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + + // no change, back to published + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode)); - Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); - // act + // act - content.PublishValues(); - contentService.SaveAndPublish(content); + content.PublishValues(langUk.IsoCode); + contentService.SaveAndPublish(content); + + content2 = contentService.GetById(content.Id); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + + // now, uk is no more edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true)); + + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - // now it has publish name for invariant neutral - - content2 = contentService.GetById(content.Id); - Assert.AreEqual("Home US", content2.PublishName); + // act - // act + content.SetName(langUk.IsoCode, "name-uk3"); + contentService.Save(content); + + content2 = contentService.GetById(content.Id); - content.SetName(null, "Home US2"); - content.SetName(langFr.IsoCode, "name-fr2"); - content.SetName(langUk.IsoCode, "name-uk2"); - content.SetValue("author", "Barack Obama2"); - content.SetValue("prop", "value-fr2", langFr.IsoCode); - content.SetValue("prop", "value-uk2", langUk.IsoCode); - contentService.Save(content); - - // content has been saved, - // it has updated names, unchanged publishNames, and published cultures + // changing the name = edited! + Assert.IsTrue(content.IsCultureEdited(langUk.IsoCode)); + Assert.IsTrue(content2.IsCultureEdited(langUk.IsoCode)); + } - content2 = contentService.GetById(content.Id); - - Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); - - Assert.AreEqual("Home US", content2.PublishName); - Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - - Assert.AreEqual("Barack Obama2", content2.GetValue("author")); - Assert.AreEqual("Barack Obama", content2.GetValue("author", published: true)); - - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); - - Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode)); - Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); - - // act - // cannot just 'save' since we are changing what's published! - - content.ClearPublishedValues(langFr.IsoCode); - contentService.SaveAndPublish(content); - - // content has been published, - // the french culture is gone - - content2 = contentService.GetById(content.Id); - - Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); - - Assert.AreEqual("Home US", content2.PublishName); - Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); - Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); - - Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); - Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); - - // act - - contentService.Unpublish(content); - - // content has been unpublished, - // but properties, names, etc. retain their 'published' values so the content - // can be re-published in its exact original state (before being unpublished) - // - // BEWARE! - // in order for a content to be unpublished as a whole, and then republished in - // its exact previous state, properties and names etc. retain their published - // values even though the content is not published - hence many things being - // non-null or true below - always check against content.Published to be sure - - content2 = contentService.GetById(content.Id); - - Assert.IsFalse(content2.Published); - - Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); - - Assert.AreEqual("Home US", content2.PublishName); // not null, see note above - Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); // not null, see note above - - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); - Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); // has value, see note above - - Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); - Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); // still true, see note above - - // act - - contentService.SaveAndPublish(content); - - // content has been re-published, - // everything is back to what it was before being unpublished - - content2 = contentService.GetById(content.Id); - - Assert.IsTrue(content2.Published); - - Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); - - Assert.AreEqual("Home US", content2.PublishName); - Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); - Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); - - Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); - Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); + private void AssertPerCulture(IContent item, Func getter, params (ILanguage Language, bool Result)[] testCases) + { + foreach (var testCase in testCases) + { + var value = getter(item, testCase.Language.IsoCode); + Assert.AreEqual(testCase.Result, value, $"Expected {testCase.Result} and got {value} for culture {testCase.Language.IsoCode}."); + } } private IEnumerable CreateContentHierarchy() diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index 124c77846d..8d8e127131 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -457,8 +457,8 @@ namespace Umbraco.Tests.Services ServiceContext.ContentTypeService.Save(contentType); var c1 = MockedContent.CreateSimpleContent(contentType, "Test", -1); - c1.SetName(_langFr.Id, "Test - FR"); - c1.SetName(_langEs.Id, "Test - ES"); + c1.SetName(_langFr.IsoCode, "Test - FR"); + c1.SetName(_langEs.IsoCode, "Test - ES"); ServiceContext.ContentService.Save(c1); var result = service.Get(c1.Id, UmbracoObjectTypes.Document); @@ -486,8 +486,8 @@ namespace Umbraco.Tests.Services var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root); if (i % 2 == 0) { - c1.SetName(_langFr.Id, "Test " + i + " - FR"); - c1.SetName(_langEs.Id, "Test " + i + " - ES"); + c1.SetName(_langFr.IsoCode, "Test " + i + " - FR"); + c1.SetName(_langEs.IsoCode, "Test " + i + " - ES"); } ServiceContext.ContentService.Save(c1); } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 467562428d..6ce5f049e8 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -956,7 +956,8 @@ namespace Umbraco.Web.Editors //set the name according to the culture settings if (contentItem.LanguageId.HasValue && contentItem.PersistedContent.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) { - contentItem.PersistedContent.SetName(contentItem.LanguageId, contentItem.Name); + var culture = Services.LocalizationService.GetLanguageById(contentItem.LanguageId.Value).IsoCode; + contentItem.PersistedContent.SetName(culture, contentItem.Name); } else { diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs index 125db912be..41383764bb 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs @@ -12,14 +12,8 @@ namespace Umbraco.Web.Models.Mapping { public string Resolve(IContent source, ContentItemDisplay destination, string destMember, ResolutionContext context) { - var langId = context.GetLanguageId(); - if (langId.HasValue && source.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) - { - //return the culture name being requested - return source.GetName(langId); - } - - return source.Name; + var culture = context.GetCulture(); + return source.GetName(culture); } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs index 72842c5354..1348dbb8a9 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs @@ -163,7 +163,7 @@ namespace Umbraco.Web.Models.Mapping // fixme not so clean really var isPublishing = typeof(IContentType).IsAssignableFrom(typeof(TDestination)); - return mapping + mapping = mapping //only map id if set to something higher then zero .ForMember(dest => dest.Id, opt => opt.Condition(src => (Convert.ToInt32(src.Id) > 0))) .ForMember(dest => dest.Id, opt => opt.MapFrom(src => Convert.ToInt32(src.Id))) @@ -179,10 +179,14 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.PropertyGroups, opt => opt.Ignore()) .ForMember(dest => dest.NoGroupPropertyTypes, opt => opt.Ignore()) // ignore, composition is managed in AfterMapContentTypeSaveToEntity - .ForMember(dest => dest.ContentTypeComposition, opt => opt.Ignore()) + .ForMember(dest => dest.ContentTypeComposition, opt => opt.Ignore()); - .ForMember(dto => dto.Variations, opt => opt.ResolveUsing>()) + // ignore for members + mapping = typeof(TDestination) == typeof(IMemberType) + ? mapping.ForMember(dto => dto.Variations, opt => opt.Ignore()) + : mapping.ForMember(dto => dto.Variations, opt => opt.ResolveUsing>()); + mapping = mapping .ForMember( dest => dest.AllowedContentTypes, opt => opt.MapFrom(src => src.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i)))) @@ -257,6 +261,8 @@ namespace Umbraco.Web.Models.Mapping // because all property collections were rebuilt, there is no need to remove // some old properties, they are just gone and will be cleared by the repository }); + + return mapping; } private static PropertyGroup MapSaveGroup(PropertyGroupBasic sourceGroup, IEnumerable destOrigGroups) From 7b82208677a033b0ee1c2bdcd40ffe0120e5098c Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 24 Apr 2018 17:43:08 +0200 Subject: [PATCH 33/97] Implement getting avail/edit/publish cultures --- src/Umbraco.Core/Models/Content.cs | 85 ++++++++++++++++------------- src/Umbraco.Core/Models/IContent.cs | 27 ++++++++- 2 files changed, 72 insertions(+), 40 deletions(-) diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 22c409d8b1..4ead233d6a 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Models private bool _published; private PublishedState _publishedState; private DateTime? _releaseDate; - private DateTime? _expireDate; + private DateTime? _expireDate; private Dictionary _publishInfos; private HashSet _edited; @@ -196,12 +196,12 @@ namespace Umbraco.Core.Models [IgnoreDataMember] public ITemplate PublishTemplate { get; internal set; } - + [IgnoreDataMember] public string PublishName { get; internal set; } // sets publish infos - // internal for repositories + // internal for repositories // clear by clearing name internal void SetPublishInfos(string culture, string name, DateTime date) { @@ -222,12 +222,12 @@ namespace Umbraco.Core.Models _publishInfos[culture] = (name, date); } - + /// [IgnoreDataMember] //public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames; public IReadOnlyDictionary PublishNames => _publishInfos?.ToDictionary(x => x.Key, x => x.Value.Name) ?? NoNames; - + /// public string GetPublishName(string culture) { @@ -235,7 +235,7 @@ namespace Umbraco.Core.Models if (_publishInfos == null) return null; return _publishInfos.TryGetValue(culture, out var infos) ? infos.Name : null; } - + // clears a publish name private void ClearPublishName(string culture) { @@ -250,18 +250,18 @@ namespace Umbraco.Core.Models if (_publishInfos.Count == 0) _publishInfos = null; } - + // clears all publish names private void ClearPublishNames() { PublishName = null; _publishInfos = null; } - + /// public bool IsCulturePublished(string culture) => !string.IsNullOrWhiteSpace(GetPublishName(culture)); - + /// public DateTime GetDateCulturePublished(string culture) { @@ -270,26 +270,35 @@ namespace Umbraco.Core.Models throw new InvalidOperationException($"Culture \"{culture}\" is not published."); } + /// + public IEnumerable PublishedCultures => _publishInfos.Keys; + /// public bool IsCultureEdited(string culture) { return string.IsNullOrWhiteSpace(GetPublishName(culture)) || (_edited != null && _edited.Contains(culture)); - } - + } + // sets a publish edited internal void SetCultureEdited(string culture) { if (_edited == null) _edited = new HashSet(StringComparer.OrdinalIgnoreCase); _edited.Add(culture); - } - + } + // sets all publish edited internal void SetCultureEdited(IEnumerable cultures) { _edited = new HashSet(cultures, StringComparer.OrdinalIgnoreCase); } - + + /// + public IEnumerable EditedCultures => Names.Keys.Where(IsCultureEdited); + + /// + public IEnumerable AvailableCultures => Names.Keys; + [IgnoreDataMember] public int PublishedVersionId { get; internal set; } @@ -303,7 +312,7 @@ namespace Umbraco.Core.Models if (ValidateAll().Any()) return false; - // Name and PublishName are managed by the repository, but Names and PublishNames + // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. if (string.IsNullOrWhiteSpace(Name)) throw new InvalidOperationException($"Cannot publish invariant culture without a name."); @@ -319,21 +328,21 @@ namespace Umbraco.Core.Models // property.PublishAllValues only deals with supported variations (if any) foreach (var property in Properties) property.PublishAllValues(); - + _publishedState = PublishedState.Publishing; return true; } /// public virtual bool PublishValues(string culture = null, string segment = null) - { - // the variation should be supported by the content type - ContentType.ValidateVariation(culture, segment, throwIfInvalid: true); - + { + // the variation should be supported by the content type + ContentType.ValidateVariation(culture, segment, throwIfInvalid: true); + // the values we want to publish should be valid if (Validate(culture, segment).Any()) return false; - + // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. if (segment == null) @@ -347,18 +356,18 @@ namespace Umbraco.Core.Models // property.PublishValue throws on invalid variation, so filter them out foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(culture, segment, throwIfInvalid: false))) property.PublishValue(culture, segment); - + _publishedState = PublishedState.Publishing; return true; } /// public virtual bool PublishCultureValues(string culture = null) - { + { // the values we want to publish should be valid if (ValidateCulture(culture).Any()) return false; - + // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. var name = GetName(culture); @@ -369,7 +378,7 @@ namespace Umbraco.Core.Models // property.PublishCultureValues only deals with supported variations (if any) foreach (var property in Properties) property.PublishCultureValues(culture); - + _publishedState = PublishedState.Publishing; return true; } @@ -383,8 +392,8 @@ namespace Umbraco.Core.Models // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - ClearPublishNames(); - + ClearPublishNames(); + _publishedState = PublishedState.Publishing; } @@ -393,7 +402,7 @@ namespace Umbraco.Core.Models { // the variation should be supported by the content type ContentType.ValidateVariation(culture, segment, throwIfInvalid: true); - + // property.ClearPublishedValue throws on invalid variation, so filter them out foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(culture, segment, throwIfInvalid: false))) property.ClearPublishedValue(culture, segment); @@ -415,10 +424,10 @@ namespace Umbraco.Core.Models // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. ClearPublishName(culture); - + _publishedState = PublishedState.Publishing; } - + private bool CopyingFromSelf(IContent other) { // copying from the same Id and VersionPk @@ -456,10 +465,10 @@ namespace Umbraco.Core.Models var value = published ? pvalue.PublishedValue : pvalue.EditedValue; SetValue(alias, value, pvalue.Culture, pvalue.Segment); } - } - + } + // copy names - ClearNames(); + ClearNames(); foreach (var (languageId, name) in other.Names) SetName(languageId, name); Name = other.Name; @@ -498,9 +507,9 @@ namespace Umbraco.Core.Models var alias = otherProperty.PropertyType.Alias; SetValue(alias, otherProperty.GetValue(culture, segment, published), culture, segment); - } + } - // copy name + // copy name SetName(culture, other.GetName(culture)); } @@ -532,9 +541,9 @@ namespace Umbraco.Core.Models var value = published ? pvalue.PublishedValue : pvalue.EditedValue; SetValue(alias, value, pvalue.Culture, pvalue.Segment); } - } - - // copy name + } + + // copy name SetName(culture, other.GetName(culture)); } diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index ab995b9b04..25f980a597 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -90,10 +90,18 @@ namespace Umbraco.Core.Models /// bool IsCulturePublished(string culture); - // fixme doc + /// + /// Gets the date a culture was published. + /// DateTime GetDateCulturePublished(string culture); - // fixme doc + /// + /// Gets a value indicated whether a given culture is edited. + /// + /// + /// A culture is edited when it is not published, or when it is published but + /// it has changes. + /// bool IsCultureEdited(string culture); /// @@ -114,6 +122,21 @@ namespace Umbraco.Core.Models /// name, which must be get via the property. /// IReadOnlyDictionary PublishNames { get; } + + /// + /// Gets the available cultures. + /// + IEnumerable AvailableCultures { get; } + + /// + /// Gets the published cultures. + /// + IEnumerable PublishedCultures { get; } + + /// + /// Gets the edited cultures. + /// + IEnumerable EditedCultures { get; } // fixme - these two should move to some kind of service From ef2fd52482efd917ca08e94446f50378e5cc28e0 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 24 Apr 2018 17:59:26 +0200 Subject: [PATCH 34/97] Cleanup --- .../Implement/ContentRepositoryBase.cs | 3 +- .../Implement/DataTypeRepository.cs | 3 +- .../Implement/EntityRepository.cs | 156 +++++++----------- 3 files changed, 61 insertions(+), 101 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 84ff426e91..270e406a24 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -20,6 +20,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; +using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -852,7 +853,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected virtual string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) { var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.EnsureUniqueNodeName", tsql => tsql - .Select(x => NPocoSqlExtensions.Statics.Alias(x.NodeId, "id"), x => NPocoSqlExtensions.Statics.Alias(x.Text, "name")) + .Select(x => Alias(x.NodeId, "id"), x => Alias(x.Text, "name")) .From() .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType") && x.ParentId == SqlTemplate.Arg("parentId"))); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs index 86444a4cc3..4556c78fe6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Persistence.Querying; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; +using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -280,7 +281,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private string EnsureUniqueNodeName(string nodeName, int id = 0) { var template = SqlContext.Templates.Get("Umbraco.Core.DataTypeDefinitionRepository.EnsureUniqueNodeName", tsql => tsql - .Select(x => NPocoSqlExtensions.Statics.Alias(x.NodeId, "id"), x => NPocoSqlExtensions.Statics.Alias(x.Text, "name")) + .Select(x => Alias(x.NodeId, "id"), x => Alias(x.Text, "name")) .From() .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType"))); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs index cb0d19cc2e..09711cf9e1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Scoping; +using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -128,7 +129,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var dtos = page.Items; var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); - //TODO: For isContent will we need to build up the variation info? + //TODO: For isContent will we need to build up the variation info? if (isMedia) BuildProperties(entities, dtos); @@ -144,35 +145,36 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return dto == null ? null : BuildEntity(false, false, dto); } + private IEntitySlim GetEntity(Sql sql, bool isContent, bool isMedia) + { + //isContent is going to return a 1:M result now with the variants so we need to do different things + if (isContent) + { + var dtos = Database.FetchOneToMany( + ddto => ddto.VariationInfo, + ddto => ddto.VersionId, + sql); + return dtos.Count == 0 ? null : BuildDocumentEntity(dtos[0]); + } + + var dto = Database.FirstOrDefault(sql); + if (dto == null) return null; + + var entity = BuildEntity(false, isMedia, dto); + + if (isMedia) + BuildProperties(entity, dto); + + return entity; + } + public IEntitySlim Get(Guid key, Guid objectTypeId) { var isContent = objectTypeId == Constants.ObjectTypes.Document || objectTypeId == Constants.ObjectTypes.DocumentBlueprint; var isMedia = objectTypeId == Constants.ObjectTypes.Media; var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, key); - - //isContent is going to return a 1:M result now with the variants so we need to do different things - if (isContent) - { - var dtos = Database.FetchOneToMany( - dto => dto.VariationInfo, - dto => dto.VersionId, - sql); - if (dtos.Count == 0) return null; - return BuildDocumentEntity(dtos[0]); - } - else - { - var dto = Database.FirstOrDefault(sql); - if (dto == null) return null; - - var entity = BuildEntity(isContent, isMedia, dto); - - if (isMedia) - BuildProperties(entity, dto); - - return entity; - } + return GetEntity(sql, isContent, isMedia); } public virtual IEntitySlim Get(int id) @@ -188,30 +190,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var isMedia = objectTypeId == Constants.ObjectTypes.Media; var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, id); - - //isContent is going to return a 1:M result now with the variants so we need to do different things - if (isContent) - { - var dtos = Database.FetchOneToMany( - dto => dto.VariationInfo, - dto => dto.VersionId, - sql); - if (dtos.Count == 0) return null; - return BuildDocumentEntity(dtos[0]); - } - else - { - var dto = Database.FirstOrDefault(sql); - if (dto == null) return null; - - var entity = BuildEntity(isContent, isMedia, dto); - - if (isMedia) - BuildProperties(entity, dto); - - return entity; - } - + return GetEntity(sql, isContent, isMedia); } public virtual IEnumerable GetAll(Guid objectType, params int[] ids) @@ -228,36 +207,38 @@ namespace Umbraco.Core.Persistence.Repositories.Implement : PerformGetAll(objectType); } + private IEnumerable GetEntities(Sql sql, bool isContent, bool isMedia) + { + //isContent is going to return a 1:M result now with the variants so we need to do different things + if (isContent) + { + var cdtos = Database.FetchOneToMany( + dto => dto.VariationInfo, + dto => dto.VersionId, + sql); + return cdtos.Count == 0 + ? Enumerable.Empty() + : cdtos.Select(BuildDocumentEntity).ToArray(); + } + + var dtos = Database.Fetch(sql); + if (dtos.Count == 0) return Enumerable.Empty(); + + var entities = dtos.Select(x => BuildEntity(false, isMedia, x)).ToArray(); + + if (isMedia) + BuildProperties(entities, dtos); + + return entities; + } + private IEnumerable PerformGetAll(Guid objectType, Action> filter = null) { var isContent = objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint; var isMedia = objectType == Constants.ObjectTypes.Media; var sql = GetFullSqlForEntityType(isContent, isMedia, objectType, filter); - - //isContent is going to return a 1:M result now with the variants so we need to do different things - if (isContent) - { - var dtos = Database.FetchOneToMany( - dto => dto.VariationInfo, - dto => dto.VersionId, - sql); - if (dtos.Count == 0) return Enumerable.Empty(); - var entities = dtos.Select(x => BuildDocumentEntity(x)).ToArray(); - return entities; - } - else - { - var dtos = Database.Fetch(sql); - if (dtos.Count == 0) return Enumerable.Empty(); - - var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); - - if (isMedia) - BuildProperties(entities, dtos); - - return entities; - } + return GetEntities(sql, isContent, isMedia); } public virtual IEnumerable GetAllPaths(Guid objectType, params int[] ids) @@ -302,30 +283,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement sql = translator.Translate(); sql = AddGroupBy(isContent, isMedia, sql); - //isContent is going to return a 1:M result now with the variants so we need to do different things - if (isContent) - { - var dtos = Database.FetchOneToMany( - dto => dto.VariationInfo, - dto => dto.VersionId, - sql); - if (dtos.Count == 0) return Enumerable.Empty(); - var entities = dtos.Select(x => BuildDocumentEntity(x)).ToArray(); - return entities; - } - else - { - var dtos = Database.Fetch(sql); - - if (dtos.Count == 0) return Enumerable.Empty(); - - var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); - - if (isMedia) - BuildProperties(entities, dtos); - - return entities; - } + return GetEntities(sql, isContent, isMedia); } public UmbracoObjectTypes GetObjectType(int id) @@ -554,9 +512,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .AndSelect(x => x.Published, x => x.Edited) //This MUST come last in the select statements since we will end up with a 1:M query .AndSelect( - x => NPocoSqlExtensions.Statics.Alias(x.Id, "versionCultureId"), - x => NPocoSqlExtensions.Statics.Alias(x.LanguageId, "versionCultureLangId"), - x => NPocoSqlExtensions.Statics.Alias(x.Name, "versionCultureName")); + x => Alias(x.Id, "versionCultureId"), + x => Alias(x.LanguageId, "versionCultureLangId"), + x => Alias(x.Name, "versionCultureName")); } } From aba5e849c1ce839653514455db83a94bc0771c63 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 24 Apr 2018 18:51:45 +0200 Subject: [PATCH 35/97] Fix tests --- src/Umbraco.Core/Models/Content.cs | 38 +++++++++++++++---- .../Composing/TypeLoaderTests.cs | 2 +- src/Umbraco.Tests/Models/VariationTests.cs | 2 + .../Mapping/ContentTypeProfileExtensions.cs | 13 +++++-- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 142d7540c2..f5a38a96cb 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -275,6 +275,19 @@ namespace Umbraco.Core.Models if (ValidateAll().Any()) return false; + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + if (string.IsNullOrWhiteSpace(Name)) + throw new InvalidOperationException($"Cannot publish invariant culture without a name."); + PublishName = Name; + foreach (var (languageId, name) in Names) + { + if (string.IsNullOrWhiteSpace(name)) + throw new InvalidOperationException($"Cannot publish {languageId} culture without a name."); + SetPublishName(languageId, name); + } + + // property.PublishAllValues only deals with supported variations (if any) foreach (var property in Properties) property.PublishAllValues(); @@ -298,15 +311,21 @@ namespace Umbraco.Core.Models // the values we want to publish should be valid if (Validate(languageId, segment).Any()) return false; + + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + if (segment == null) + { + var name = GetName(languageId); + if (string.IsNullOrWhiteSpace(name)) + throw new InvalidOperationException($"Cannot publish {languageId?.ToString() ?? "invariant"} culture without a name."); + SetPublishName(languageId, name); + } // property.PublishValue throws on invalid variation, so filter them out foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(languageId, segment, throwIfInvalid: false))) property.PublishValue(languageId, segment); - // Name and PublishName are managed by the repository, but Names and PublishNames - // must be managed here as they depend on the existing / supported variations. - SetPublishName(languageId, GetName(languageId)); - _publishedState = PublishedState.Publishing; return true; } @@ -317,15 +336,18 @@ namespace Umbraco.Core.Models // the values we want to publish should be valid if (ValidateCulture(languageId).Any()) return false; + + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + var name = GetName(languageId); + if (string.IsNullOrWhiteSpace(name)) + throw new InvalidOperationException($"Cannot publish {languageId?.ToString() ?? "invariant"} culture without a name."); + SetPublishName(languageId, name); // property.PublishCultureValues only deals with supported variations (if any) foreach (var property in Properties) property.PublishCultureValues(languageId); - // Name and PublishName are managed by the repository, but Names and PublishNames - // must be managed here as they depend on the existing / supported variations. - SetPublishName(languageId, GetName(languageId)); - _publishedState = PublishedState.Publishing; return true; } diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 8965d60018..6fba071709 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -286,7 +286,7 @@ AnotherContentFinder public void GetDataEditors() { var types = _typeLoader.GetDataEditors(); - Assert.AreEqual(42, types.Count()); + Assert.AreEqual(43, types.Count()); } [Test] diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 3b293621d5..d0af5ee2a4 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -236,6 +236,8 @@ namespace Umbraco.Tests.Models // can publish value // and get edited and published values + Assert.Throws(() => content.PublishValues(1)); // no name + content.SetName(1, "name-fr"); content.PublishValues(1); Assert.AreEqual("b", content.GetValue("prop")); Assert.IsNull(content.GetValue("prop", published: true)); diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs index 72842c5354..83345988be 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs @@ -163,7 +163,7 @@ namespace Umbraco.Web.Models.Mapping // fixme not so clean really var isPublishing = typeof(IContentType).IsAssignableFrom(typeof(TDestination)); - return mapping + mapping = mapping //only map id if set to something higher then zero .ForMember(dest => dest.Id, opt => opt.Condition(src => (Convert.ToInt32(src.Id) > 0))) .ForMember(dest => dest.Id, opt => opt.MapFrom(src => Convert.ToInt32(src.Id))) @@ -179,10 +179,15 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.PropertyGroups, opt => opt.Ignore()) .ForMember(dest => dest.NoGroupPropertyTypes, opt => opt.Ignore()) // ignore, composition is managed in AfterMapContentTypeSaveToEntity - .ForMember(dest => dest.ContentTypeComposition, opt => opt.Ignore()) + .ForMember(dest => dest.ContentTypeComposition, opt => opt.Ignore()); - .ForMember(dto => dto.Variations, opt => opt.ResolveUsing>()) + // ignore for members + mapping = typeof(TDestination) == typeof(IMemberType) + ? mapping.ForMember(dto => dto.Variations, opt => opt.Ignore()) + : mapping.ForMember(dto => dto.Variations, opt => opt.ResolveUsing>()); + + mapping = mapping .ForMember( dest => dest.AllowedContentTypes, opt => opt.MapFrom(src => src.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i)))) @@ -257,6 +262,8 @@ namespace Umbraco.Web.Models.Mapping // because all property collections were rebuilt, there is no need to remove // some old properties, they are just gone and will be cleared by the repository }); + + return mapping; } private static PropertyGroup MapSaveGroup(PropertyGroupBasic sourceGroup, IEnumerable destOrigGroups) From f3bf5d86d2d29921f52afcced83d4bd0d43ea376 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 25 Apr 2018 15:25:47 +0200 Subject: [PATCH 36/97] Cleanup BTree --- .../DataSource/BTree.ContentDataSerializer.cs | 38 +++ .../BTree.ContentNodeKitSerializer.cs | 57 ++++ ....DictionaryOfCultureVariationSerializer.cs | 46 +++ ...Tree.DictionaryOfPropertyDataSerializer.cs | 71 +++++ .../NuCache/DataSource/BTree.cs | 280 +----------------- .../NuCache/DataSource/SerializerBase.cs | 107 +++++++ src/Umbraco.Web/Umbraco.Web.csproj | 5 + 7 files changed, 325 insertions(+), 279 deletions(-) create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentNodeKitSerializer.cs create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/SerializerBase.cs diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs new file mode 100644 index 0000000000..7c793b69bd --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs @@ -0,0 +1,38 @@ +using System.IO; +using CSharpTest.Net.Serialization; + +namespace Umbraco.Web.PublishedCache.NuCache.DataSource +{ + class ContentDataSerializer : ISerializer + { + private static readonly DictionaryOfPropertyDataSerializer PropertiesSerializer = new DictionaryOfPropertyDataSerializer(); + private static readonly DictionaryOfCultureVariationSerializer CultureVariationsSerializer = new DictionaryOfCultureVariationSerializer(); + + public ContentData ReadFrom(Stream stream) + { + return new ContentData + { + Published = PrimitiveSerializer.Boolean.ReadFrom(stream), + Name = PrimitiveSerializer.String.ReadFrom(stream), + VersionId = PrimitiveSerializer.Int32.ReadFrom(stream), + VersionDate = PrimitiveSerializer.DateTime.ReadFrom(stream), + WriterId = PrimitiveSerializer.Int32.ReadFrom(stream), + TemplateId = PrimitiveSerializer.Int32.ReadFrom(stream), + Properties = PropertiesSerializer.ReadFrom(stream), + CultureInfos = CultureVariationsSerializer.ReadFrom(stream) + }; + } + + public void WriteTo(ContentData value, Stream stream) + { + PrimitiveSerializer.Boolean.WriteTo(value.Published, stream); + PrimitiveSerializer.String.WriteTo(value.Name, stream); + PrimitiveSerializer.Int32.WriteTo(value.VersionId, stream); + PrimitiveSerializer.DateTime.WriteTo(value.VersionDate, stream); + PrimitiveSerializer.Int32.WriteTo(value.WriterId, stream); + PrimitiveSerializer.Int32.WriteTo(value.TemplateId, stream); + PropertiesSerializer.WriteTo(value.Properties, stream); + CultureVariationsSerializer.WriteTo(value.CultureInfos, stream); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentNodeKitSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentNodeKitSerializer.cs new file mode 100644 index 0000000000..4303f97d3c --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentNodeKitSerializer.cs @@ -0,0 +1,57 @@ +using System.IO; +using CSharpTest.Net.Serialization; + +namespace Umbraco.Web.PublishedCache.NuCache.DataSource +{ + internal class ContentNodeKitSerializer : ISerializer + { + static readonly ContentDataSerializer DataSerializer = new ContentDataSerializer(); + //static readonly ListOfIntSerializer ChildContentIdsSerializer = new ListOfIntSerializer(); + + public ContentNodeKit ReadFrom(Stream stream) + { + var kit = new ContentNodeKit + { + Node = new ContentNode( + PrimitiveSerializer.Int32.ReadFrom(stream), // id + PrimitiveSerializer.Guid.ReadFrom(stream), // uid + PrimitiveSerializer.Int32.ReadFrom(stream), // level + PrimitiveSerializer.String.ReadFrom(stream), // path + PrimitiveSerializer.Int32.ReadFrom(stream), // sort order + PrimitiveSerializer.Int32.ReadFrom(stream), // parent id + PrimitiveSerializer.DateTime.ReadFrom(stream), // date created + PrimitiveSerializer.Int32.ReadFrom(stream) // creator id + ), + ContentTypeId = PrimitiveSerializer.Int32.ReadFrom(stream) + }; + var hasDraft = PrimitiveSerializer.Boolean.ReadFrom(stream); + if (hasDraft) + kit.DraftData = DataSerializer.ReadFrom(stream); + var hasPublished = PrimitiveSerializer.Boolean.ReadFrom(stream); + if (hasPublished) + kit.PublishedData = DataSerializer.ReadFrom(stream); + return kit; + } + + public void WriteTo(ContentNodeKit value, Stream stream) + { + PrimitiveSerializer.Int32.WriteTo(value.Node.Id, stream); + PrimitiveSerializer.Guid.WriteTo(value.Node.Uid, stream); + PrimitiveSerializer.Int32.WriteTo(value.Node.Level, stream); + PrimitiveSerializer.String.WriteTo(value.Node.Path, stream); + PrimitiveSerializer.Int32.WriteTo(value.Node.SortOrder, stream); + PrimitiveSerializer.Int32.WriteTo(value.Node.ParentContentId, stream); + PrimitiveSerializer.DateTime.WriteTo(value.Node.CreateDate, stream); + PrimitiveSerializer.Int32.WriteTo(value.Node.CreatorId, stream); + PrimitiveSerializer.Int32.WriteTo(value.ContentTypeId, stream); + + PrimitiveSerializer.Boolean.WriteTo(value.DraftData != null, stream); + if (value.DraftData != null) + DataSerializer.WriteTo(value.DraftData, stream); + + PrimitiveSerializer.Boolean.WriteTo(value.PublishedData != null, stream); + if (value.PublishedData != null) + DataSerializer.WriteTo(value.PublishedData, stream); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs new file mode 100644 index 0000000000..a835886254 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.IO; +using CSharpTest.Net.Serialization; +using Umbraco.Core; + +namespace Umbraco.Web.PublishedCache.NuCache.DataSource +{ + internal class DictionaryOfCultureVariationSerializer : SerializerBase, ISerializer> + { + public IReadOnlyDictionary ReadFrom(Stream stream) + { + var dict = new Dictionary(); + + // read variations count + var pcount = PrimitiveSerializer.Int32.ReadFrom(stream); + + // read each variation + for (var i = 0; i < pcount; i++) + { + var languageId = PrimitiveSerializer.String.ReadFrom(stream); + var cultureVariation = new CultureVariation { Name = ReadStringObject(stream) }; + dict[languageId] = cultureVariation; + } + return dict; + } + + private static readonly IReadOnlyDictionary Empty = new Dictionary(); + + public void WriteTo(IReadOnlyDictionary value, Stream stream) + { + var variations = value ?? Empty; + + // write variations count + PrimitiveSerializer.Int32.WriteTo(variations.Count, stream); + + // write each variation + foreach (var (culture, variation) in variations) + { + // fixme - it's weird we're dealing with cultures here, and languageId in properties + + PrimitiveSerializer.String.WriteTo(culture, stream); // should never be null + WriteObject(variation.Name, stream); // write an object in case it's null (though... should not happen) + } + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs new file mode 100644 index 0000000000..0b4d391a3a --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.IO; +using CSharpTest.Net.Serialization; +using Umbraco.Core; + +namespace Umbraco.Web.PublishedCache.NuCache.DataSource +{ + internal class DictionaryOfPropertyDataSerializer : SerializerBase, ISerializer> + { + public IDictionary ReadFrom(Stream stream) + { + var dict = new Dictionary(); + + // read properties count + var pcount = PrimitiveSerializer.Int32.ReadFrom(stream); + + // read each property + for (var i = 0; i < pcount; i++) + { + // read property alias + var key = PrimitiveSerializer.String.ReadFrom(stream); + + // read values count + var vcount = PrimitiveSerializer.Int32.ReadFrom(stream); + + // create pdata and add to the dictionary + var pdatas = new List(); + + // for each value, read and add to pdata + for (var j = 0; j < vcount; j++) + { + var pdata = new PropertyData(); + pdatas.Add(pdata); + + // everything that can be null is read/written as object + pdata.LanguageId = ReadIntObject(stream); + pdata.Segment = ReadStringObject(stream); + pdata.Value = ReadObject(stream); + } + + dict[key] = pdatas.ToArray(); + } + return dict; + } + + public void WriteTo(IDictionary value, Stream stream) + { + // write properties count + PrimitiveSerializer.Int32.WriteTo(value.Count, stream); + + // write each property + foreach (var (alias, values) in value) + { + // write alias + PrimitiveSerializer.String.WriteTo(alias, stream); + + // write values count + PrimitiveSerializer.Int32.WriteTo(values.Length, stream); + + // write each value + foreach (var pdata in values) + { + // everything that can be null is read/written as object + WriteObject(pdata.LanguageId, stream); + WriteObject(pdata.Segment, stream); + WriteObject(pdata.Value, stream); + } + } + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs index 7329033405..1b17e0c124 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using CSharpTest.Net.Collections; +using CSharpTest.Net.Collections; using CSharpTest.Net.Serialization; namespace Umbraco.Web.PublishedCache.NuCache.DataSource @@ -28,91 +25,6 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return tree; } - private class ContentNodeKitSerializer : ISerializer - { - static readonly ContentDataSerializer DataSerializer = new ContentDataSerializer(); - //static readonly ListOfIntSerializer ChildContentIdsSerializer = new ListOfIntSerializer(); - - public ContentNodeKit ReadFrom(Stream stream) - { - var kit = new ContentNodeKit - { - Node = new ContentNode( - PrimitiveSerializer.Int32.ReadFrom(stream), // id - PrimitiveSerializer.Guid.ReadFrom(stream), // uid - PrimitiveSerializer.Int32.ReadFrom(stream), // level - PrimitiveSerializer.String.ReadFrom(stream), // path - PrimitiveSerializer.Int32.ReadFrom(stream), // sort order - PrimitiveSerializer.Int32.ReadFrom(stream), // parent id - PrimitiveSerializer.DateTime.ReadFrom(stream), // date created - PrimitiveSerializer.Int32.ReadFrom(stream) // creator id - ), - ContentTypeId = PrimitiveSerializer.Int32.ReadFrom(stream) - }; - var hasDraft = PrimitiveSerializer.Boolean.ReadFrom(stream); - if (hasDraft) - kit.DraftData = DataSerializer.ReadFrom(stream); - var hasPublished = PrimitiveSerializer.Boolean.ReadFrom(stream); - if (hasPublished) - kit.PublishedData = DataSerializer.ReadFrom(stream); - return kit; - } - - public void WriteTo(ContentNodeKit value, Stream stream) - { - PrimitiveSerializer.Int32.WriteTo(value.Node.Id, stream); - PrimitiveSerializer.Guid.WriteTo(value.Node.Uid, stream); - PrimitiveSerializer.Int32.WriteTo(value.Node.Level, stream); - PrimitiveSerializer.String.WriteTo(value.Node.Path, stream); - PrimitiveSerializer.Int32.WriteTo(value.Node.SortOrder, stream); - PrimitiveSerializer.Int32.WriteTo(value.Node.ParentContentId, stream); - PrimitiveSerializer.DateTime.WriteTo(value.Node.CreateDate, stream); - PrimitiveSerializer.Int32.WriteTo(value.Node.CreatorId, stream); - PrimitiveSerializer.Int32.WriteTo(value.ContentTypeId, stream); - - PrimitiveSerializer.Boolean.WriteTo(value.DraftData != null, stream); - if (value.DraftData != null) - DataSerializer.WriteTo(value.DraftData, stream); - - PrimitiveSerializer.Boolean.WriteTo(value.PublishedData != null, stream); - if (value.PublishedData != null) - DataSerializer.WriteTo(value.PublishedData, stream); - } - } - - class ContentDataSerializer : ISerializer - { - private static readonly DictionaryOfPropertyDataSerializer PropertiesSerializer = new DictionaryOfPropertyDataSerializer(); - private static readonly DictionaryOfCultureVariationSerializer CultureVariationsSerializer = new DictionaryOfCultureVariationSerializer(); - - public ContentData ReadFrom(Stream stream) - { - return new ContentData - { - Published = PrimitiveSerializer.Boolean.ReadFrom(stream), - Name = PrimitiveSerializer.String.ReadFrom(stream), - VersionId = PrimitiveSerializer.Int32.ReadFrom(stream), - VersionDate = PrimitiveSerializer.DateTime.ReadFrom(stream), - WriterId = PrimitiveSerializer.Int32.ReadFrom(stream), - TemplateId = PrimitiveSerializer.Int32.ReadFrom(stream), - Properties = PropertiesSerializer.ReadFrom(stream), - CultureInfos = CultureVariationsSerializer.ReadFrom(stream) - }; - } - - public void WriteTo(ContentData value, Stream stream) - { - PrimitiveSerializer.Boolean.WriteTo(value.Published, stream); - PrimitiveSerializer.String.WriteTo(value.Name, stream); - PrimitiveSerializer.Int32.WriteTo(value.VersionId, stream); - PrimitiveSerializer.DateTime.WriteTo(value.VersionDate, stream); - PrimitiveSerializer.Int32.WriteTo(value.WriterId, stream); - PrimitiveSerializer.Int32.WriteTo(value.TemplateId, stream); - PropertiesSerializer.WriteTo(value.Properties, stream); - CultureVariationsSerializer.WriteTo(value.CultureInfos, stream); - } - } - /* class ListOfIntSerializer : ISerializer> { @@ -133,195 +45,5 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } } */ - - private class DictionaryOfCultureVariationSerializer : ISerializer> - { - public IReadOnlyDictionary ReadFrom(Stream stream) - { - var dict = new Dictionary(); - - // read values count - var pcount = PrimitiveSerializer.Int32.ReadFrom(stream); - - // read each property - for (var i = 0; i < pcount; i++) - { - // read lang id - var key = PrimitiveSerializer.String.ReadFrom(stream); - - var val = new CultureVariation(); - - // read variation info - //TODO: This is supporting multiple properties but we only have one currently - var type = PrimitiveSerializer.Char.ReadFrom(stream); - switch(type) - { - case 'N': - val.Name = PrimitiveSerializer.String.ReadFrom(stream); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - dict[key] = val; - } - return dict; - } - - private static readonly IReadOnlyDictionary Empty = new Dictionary(); - - public void WriteTo(IReadOnlyDictionary value, Stream stream) - { - var valToSerialize = value; - if (valToSerialize == null) - { - valToSerialize = Empty; - } - - // write values count - PrimitiveSerializer.Int32.WriteTo(valToSerialize.Count, stream); - - // write each name - foreach (var kvp in valToSerialize) - { - // write alias - PrimitiveSerializer.String.WriteTo(kvp.Key, stream); - - // write name - PrimitiveSerializer.Char.WriteTo('N', stream); - PrimitiveSerializer.String.WriteTo(kvp.Value.Name, stream); - //TODO: This is supporting multiple properties but we only have one currently - } - } - - } - - private class DictionaryOfPropertyDataSerializer : ISerializer> - { - public IDictionary ReadFrom(Stream stream) - { - var dict = new Dictionary(); - - // read properties count - var pcount = PrimitiveSerializer.Int32.ReadFrom(stream); - - // read each property - for (var i = 0; i < pcount; i++) - { - // read property alias - var key = PrimitiveSerializer.String.ReadFrom(stream); - - // read values count - var vcount = PrimitiveSerializer.Int32.ReadFrom(stream); - - // create pdata and add to the dictionary - var pdatas = new List(); - - // for each value, read and add to pdata - for (var j = 0; j < vcount; j++) - { - var pdata = new PropertyData(); - pdatas.Add(pdata); - - var type = PrimitiveSerializer.Char.ReadFrom(stream); - pdata.LanguageId = (int?) ReadObject(type, stream); - type = PrimitiveSerializer.Char.ReadFrom(stream); - pdata.Segment = (string) ReadObject(type, stream); - type = PrimitiveSerializer.Char.ReadFrom(stream); - pdata.Value = ReadObject(type, stream); - } - - dict[key] = pdatas.ToArray(); - } - return dict; - } - - private object ReadObject(char type, Stream stream) - { - switch (type) - { - case 'N': - return null; - case 'S': - return PrimitiveSerializer.String.ReadFrom(stream); - case 'I': - return PrimitiveSerializer.Int32.ReadFrom(stream); - case 'L': - return PrimitiveSerializer.Int64.ReadFrom(stream); - case 'F': - return PrimitiveSerializer.Float.ReadFrom(stream); - case 'B': - return PrimitiveSerializer.Double.ReadFrom(stream); - case 'D': - return PrimitiveSerializer.DateTime.ReadFrom(stream); - default: - throw new NotSupportedException("Cannot deserialize '" + type + "' value."); - } - } - - public void WriteTo(IDictionary value, Stream stream) - { - // write properties count - PrimitiveSerializer.Int32.WriteTo(value.Count, stream); - - // write each property - foreach (var kvp in value) - { - // write alias - PrimitiveSerializer.String.WriteTo(kvp.Key, stream); - - // write values count - PrimitiveSerializer.Int32.WriteTo(kvp.Value.Length, stream); - - // write each value - foreach (var pdata in kvp.Value) - { - WriteObject(pdata.LanguageId, stream); - WriteObject(pdata.Segment, stream); - WriteObject(pdata.Value, stream); - } - } - } - - private void WriteObject(object value, Stream stream) - { - if (value == null) - { - PrimitiveSerializer.Char.WriteTo('N', stream); - } - else if (value is string stringValue) - { - PrimitiveSerializer.Char.WriteTo('S', stream); - PrimitiveSerializer.String.WriteTo(stringValue, stream); - } - else if (value is int intValue) - { - PrimitiveSerializer.Char.WriteTo('I', stream); - PrimitiveSerializer.Int32.WriteTo(intValue, stream); - } - else if (value is long longValue) - { - PrimitiveSerializer.Char.WriteTo('L', stream); - PrimitiveSerializer.Int64.WriteTo(longValue, stream); - } - else if (value is float floatValue) - { - PrimitiveSerializer.Char.WriteTo('F', stream); - PrimitiveSerializer.Float.WriteTo(floatValue, stream); - } - else if (value is double doubleValue) - { - PrimitiveSerializer.Char.WriteTo('B', stream); - PrimitiveSerializer.Double.WriteTo(doubleValue, stream); - } - else if (value is DateTime dateValue) - { - PrimitiveSerializer.Char.WriteTo('D', stream); - PrimitiveSerializer.DateTime.WriteTo(dateValue, stream); - } - else - throw new NotSupportedException("Value type " + value.GetType().FullName + " cannot be serialized."); - } - } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/SerializerBase.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/SerializerBase.cs new file mode 100644 index 0000000000..01f0bbad9b --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/SerializerBase.cs @@ -0,0 +1,107 @@ +using System; +using System.IO; +using CSharpTest.Net.Serialization; + +namespace Umbraco.Web.PublishedCache.NuCache.DataSource +{ + internal abstract class SerializerBase + { + protected string ReadString(Stream stream) => PrimitiveSerializer.String.ReadFrom(stream); + protected int ReadInt(Stream stream) => PrimitiveSerializer.Int32.ReadFrom(stream); + protected long ReadLong(Stream stream) => PrimitiveSerializer.Int64.ReadFrom(stream); + protected float ReadFloat(Stream stream) => PrimitiveSerializer.Float.ReadFrom(stream); + protected double ReadDouble(Stream stream) => PrimitiveSerializer.Double.ReadFrom(stream); + protected DateTime ReadDateTime(Stream stream) => PrimitiveSerializer.DateTime.ReadFrom(stream); + + private T? ReadObject(Stream stream, char t, Func read) + where T : struct + { + var type = PrimitiveSerializer.Char.ReadFrom(stream); + if (type == 'N') return null; + if (type != t) + throw new NotSupportedException($"Cannot deserialize type '{type}', expected '{t}'."); + return read(stream); + } + + protected string ReadStringObject(Stream stream) // required 'cos string is not a struct + { + var type = PrimitiveSerializer.Char.ReadFrom(stream); + if (type == 'N') return null; + if (type != 'S') + throw new NotSupportedException($"Cannot deserialize type '{type}', expected 'S'."); + return PrimitiveSerializer.String.ReadFrom(stream); + } + + protected int? ReadIntObject(Stream stream) => ReadObject(stream, 'I', ReadInt); + protected long? ReadLongObject(Stream stream) => ReadObject(stream, 'L', ReadLong); + protected float? ReadFloatObject(Stream stream) => ReadObject(stream, 'F', ReadFloat); + protected double? ReadDoubleObject(Stream stream) => ReadObject(stream, 'B', ReadDouble); + protected DateTime? ReadDateTimeObject(Stream stream) => ReadObject(stream, 'D', ReadDateTime); + + protected object ReadObject(Stream stream) + => ReadObject(PrimitiveSerializer.Char.ReadFrom(stream), stream); + + protected object ReadObject(char type, Stream stream) + { + switch (type) + { + case 'N': + return null; + case 'S': + return PrimitiveSerializer.String.ReadFrom(stream); + case 'I': + return PrimitiveSerializer.Int32.ReadFrom(stream); + case 'L': + return PrimitiveSerializer.Int64.ReadFrom(stream); + case 'F': + return PrimitiveSerializer.Float.ReadFrom(stream); + case 'B': + return PrimitiveSerializer.Double.ReadFrom(stream); + case 'D': + return PrimitiveSerializer.DateTime.ReadFrom(stream); + default: + throw new NotSupportedException($"Cannot deserialize unknow type '{type}'."); + } + } + + protected void WriteObject(object value, Stream stream) + { + if (value == null) + { + PrimitiveSerializer.Char.WriteTo('N', stream); + } + else if (value is string stringValue) + { + PrimitiveSerializer.Char.WriteTo('S', stream); + PrimitiveSerializer.String.WriteTo(stringValue, stream); + } + else if (value is int intValue) + { + PrimitiveSerializer.Char.WriteTo('I', stream); + PrimitiveSerializer.Int32.WriteTo(intValue, stream); + } + else if (value is long longValue) + { + PrimitiveSerializer.Char.WriteTo('L', stream); + PrimitiveSerializer.Int64.WriteTo(longValue, stream); + } + else if (value is float floatValue) + { + PrimitiveSerializer.Char.WriteTo('F', stream); + PrimitiveSerializer.Float.WriteTo(floatValue, stream); + } + else if (value is double doubleValue) + { + PrimitiveSerializer.Char.WriteTo('B', stream); + PrimitiveSerializer.Double.WriteTo(doubleValue, stream); + } + else if (value is DateTime dateValue) + { + PrimitiveSerializer.Char.WriteTo('D', stream); + PrimitiveSerializer.DateTime.WriteTo(dateValue, stream); + } + else + throw new NotSupportedException("Value type " + value.GetType().FullName + " cannot be serialized."); + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 65ca4c44b4..01e064d398 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -342,9 +342,14 @@ + + + + + From 40f94d23d4e0c28fa114b0cd3950b1198b80cb5e Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 25 Apr 2018 15:55:27 +0200 Subject: [PATCH 37/97] Cleanup --- ...SerializedData.cs => ContentNestedData.cs} | 2 +- .../NuCache/DataSource/Database.cs | 22 +++++++++---------- .../NuCache/PublishedContent.cs | 19 ++++++---------- .../NuCache/PublishedSnapshotService.cs | 14 ++++++------ src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 5 files changed, 27 insertions(+), 32 deletions(-) rename src/Umbraco.Web/PublishedCache/NuCache/DataSource/{ContentSerializedData.cs => ContentNestedData.cs} (91%) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSerializedData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs similarity index 91% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSerializedData.cs rename to src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs index b2cb22a74b..be3e813275 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSerializedData.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs @@ -6,7 +6,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// /// The content item 1:M data that is serialized to JSON /// - internal class ContentSerializedData + internal class ContentNestedData { [JsonProperty("properties")] public Dictionary PropertyData { get; set; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs index 2c4249cd08..342ad5b59f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs @@ -190,7 +190,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } else { - var deserialized = DeserializeData(dto.EditData); + var nested = DeserializeNestedData(dto.EditData); d = new ContentData { @@ -200,8 +200,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource VersionId = dto.VersionId, VersionDate = dto.EditVersionDate, WriterId = dto.EditWriterId, - Properties = deserialized.PropertyData, - CultureInfos = deserialized.CultureData + Properties = nested.PropertyData, + CultureInfos = nested.CultureData }; } @@ -215,7 +215,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } else { - var deserialized = DeserializeData(dto.PubData); + var nested = DeserializeNestedData(dto.PubData); p = new ContentData { @@ -225,8 +225,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource VersionId = dto.VersionId, VersionDate = dto.PubVersionDate, WriterId = dto.PubWriterId, - Properties = deserialized.PropertyData, - CultureInfos = deserialized.CultureData + Properties = nested.PropertyData, + CultureInfos = nested.CultureData }; } } @@ -250,7 +250,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource if (dto.EditData == null) throw new Exception("No data for media " + dto.Id); - var deserialized = DeserializeData(dto.EditData); + var nested = DeserializeNestedData(dto.EditData); var p = new ContentData { @@ -260,8 +260,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource VersionId = dto.VersionId, VersionDate = dto.EditVersionDate, WriterId = dto.CreatorId, // what-else? - Properties = deserialized.PropertyData, - CultureInfos = deserialized.CultureData + Properties = nested.PropertyData, + CultureInfos = nested.CultureData }; var n = new ContentNode(dto.Id, dto.Uid, @@ -277,7 +277,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return s; } - private static ContentSerializedData DeserializeData(string data) + private static ContentNestedData DeserializeNestedData(string data) { // by default JsonConvert will deserialize our numeric values as Int64 // which is bad, because they were Int32 in the database - take care @@ -287,7 +287,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource Converters = new List { new ForceInt32Converter() } }; - return JsonConvert.DeserializeObject(data, settings); + return JsonConvert.DeserializeObject(data, settings); } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 4502245c5a..883a43a7d5 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -35,15 +35,10 @@ namespace Umbraco.Web.PublishedCache.NuCache var properties = new List(); foreach (var propertyType in _contentNode.ContentType.PropertyTypes) { - if (contentData.Properties.TryGetValue(propertyType.Alias, out var pdatas)) - { - properties.Add(new Property(propertyType, this, pdatas, _publishedSnapshotAccessor)); - } - else - { - //it doesn't exist in our serialized json but we should add it as an empty property so they are in sync - properties.Add(new Property(propertyType, this, null, _publishedSnapshotAccessor)); - } + // add one property per property type - this is required, for the indexing to work + // if contentData supplies pdatas, use them, else use null + contentData.Properties.TryGetValue(propertyType.Alias, out var pdatas); // else will be null + properties.Add(new Property(propertyType, this, pdatas, _publishedSnapshotAccessor)); } PropertiesArray = properties.ToArray(); } @@ -265,9 +260,9 @@ namespace Umbraco.Web.PublishedCache.NuCache public override IPublishedProperty GetProperty(string alias) { var index = _contentNode.ContentType.GetPropertyIndex(alias); - if (index < 0) return null; - //fixme: This should not happen since we align the PropertiesArray with the property types in the ctor, if this does happen maybe we should throw a descriptive exception - if (index >= PropertiesArray.Length) return null; + if (index < 0) return null; // happens when 'alias' does not match a content type property alias + if (index >= PropertiesArray.Length) // should never happen - properties array must be in sync with property type + throw new IndexOutOfRangeException("Index points outside the properties array, which means the properties array is corrupt."); var property = PropertiesArray[index]; return property; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index eae0fd50b5..faff5e9793 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -1179,9 +1179,6 @@ namespace Umbraco.Web.PublishedCache.NuCache //var propertyEditorResolver = PropertyEditorResolver.Current; //var dataTypeService = ApplicationContext.Current.Services.DataTypeService; - //the dictionary that will be serialized - var data = new ContentSerializedData(); - var propertyData = new Dictionary(); foreach (var prop in content.Properties) { @@ -1216,8 +1213,6 @@ namespace Umbraco.Web.PublishedCache.NuCache propertyData[prop.Alias] = pdatas.ToArray(); } - data.PropertyData = propertyData; - var cultureData = new Dictionary(); if (content.Names != null) { @@ -1233,7 +1228,12 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - data.CultureData = cultureData; + //the dictionary that will be serialized + var nestedData = new ContentNestedData + { + PropertyData = propertyData, + CultureData = cultureData + }; var dto = new ContentNuDto { @@ -1243,7 +1243,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // note that numeric values (which are Int32) are serialized without their // type (eg "value":1234) and JsonConvert by default deserializes them as Int64 - Data = JsonConvert.SerializeObject(data) + Data = JsonConvert.SerializeObject(nestedData) }; //Core.Composing.Current.Logger.Debug(dto.Data); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 01e064d398..98afb387d4 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -346,7 +346,7 @@ - + From 23c40cbf2c5df6096e1657275c67b21af496bd64 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 26 Apr 2018 21:37:29 +1000 Subject: [PATCH 38/97] Fixes a couple issues, changes name from PublishValues to TryPublishValues --- src/Umbraco.Core/Models/Content.cs | 14 +- src/Umbraco.Core/Models/ContentExtensions.cs | 2 +- src/Umbraco.Core/Models/IContent.cs | 6 +- .../Implement/DocumentRepository.cs | 10 +- .../Publishing/ScheduledPublisher.cs | 2 +- .../Services/Implement/ContentService.cs | 4 +- .../Integration/ContentEventsTests.cs | 146 +++++++++--------- src/Umbraco.Tests/Models/VariationTests.cs | 12 +- .../NPocoTests/PetaPocoCachesTest.cs | 4 +- .../Repositories/ContentRepositoryTest.cs | 8 +- .../Scoping/ScopedNuCacheTests.cs | 4 +- src/Umbraco.Tests/Scoping/ScopedXmlTests.cs | 8 +- .../Services/ContentServiceTests.cs | 124 +++++++-------- .../Services/ContentTypeServiceTests.cs | 20 +-- .../Services/PerformanceTests.cs | 4 +- src/Umbraco.Tests/Services/TagServiceTests.cs | 14 +- src/Umbraco.Web/Editors/ContentController.cs | 14 +- .../Mapping/ContentItemDisplayNameResolver.cs | 8 +- .../WebServices/BulkPublishController.cs | 2 +- 19 files changed, 208 insertions(+), 198 deletions(-) diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 4ead233d6a..708f0ac853 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -306,11 +306,11 @@ namespace Umbraco.Core.Models public bool Blueprint { get; internal set; } /// - public virtual bool PublishAllValues() + public virtual bool TryPublishAllValues() { // the values we want to publish should be valid if (ValidateAll().Any()) - return false; + return false; //fixme this should return an attempt with error results // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. @@ -321,7 +321,8 @@ namespace Umbraco.Core.Models foreach (var (culture, name) in Names) { if (string.IsNullOrWhiteSpace(name)) - throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name."); + return false; //fixme this should return an attempt with error results + SetPublishInfos(culture, name, now); } @@ -334,14 +335,14 @@ namespace Umbraco.Core.Models } /// - public virtual bool PublishValues(string culture = null, string segment = null) + public virtual bool TryPublishValues(string culture = null, string segment = null) { // the variation should be supported by the content type ContentType.ValidateVariation(culture, segment, throwIfInvalid: true); // the values we want to publish should be valid if (Validate(culture, segment).Any()) - return false; + return false; //fixme this should return an attempt with error results // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. @@ -349,7 +350,8 @@ namespace Umbraco.Core.Models { var name = GetName(culture); if (string.IsNullOrWhiteSpace(name)) - throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name."); + return false; //fixme this should return an attempt with error results + SetPublishInfos(culture, name, DateTime.Now); } diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 0940675346..c0e4214881 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -168,7 +168,7 @@ namespace Umbraco.Core.Models public static bool HasPropertyTypeVaryingByCulture(this IContent content) { // fixme - what about CultureSegment? what about content.ContentType.Variations? - return content.PropertyTypes.Any(x => x.Variations == ContentVariation.CultureNeutral); + return content.PropertyTypes.Any(x => x.Variations.Has(ContentVariation.CultureNeutral)); } #endregion diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 25f980a597..2f3fd06f5f 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -169,7 +169,8 @@ namespace Umbraco.Core.Models /// The document must then be published via the content service. /// Values are not published if they are not valie. /// - bool PublishAllValues(); + //fixme return an Attempt with some error results if it doesn't work + bool TryPublishAllValues(); /// /// Publishes values. @@ -179,7 +180,8 @@ namespace Umbraco.Core.Models /// The document must then be published via the content service. /// Values are not published if they are not valid. /// - bool PublishValues(string culture = null, string segment = null); + //fixme return an Attempt with some error results if it doesn't work + bool TryPublishValues(string culture = null, string segment = null); /// /// Publishes the culture/any values. diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index e47e8aa33e..76321abe6a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -341,10 +341,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement (editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(culture); // insert content variations - Database.InsertBulk(GetContentVariationDtos(content, publishing)); + Database.BulkInsertRecords(GetContentVariationDtos(content, publishing)); // insert document variations - Database.InsertBulk(GetDocumentVariationDtos(content, publishing, editedCultures)); + Database.BulkInsertRecords(GetDocumentVariationDtos(content, publishing, editedCultures)); } // refresh content @@ -501,14 +501,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var deleteDocumentVariations = Sql().Delete().Where(x => x.NodeId == content.Id); Database.Execute(deleteDocumentVariations); - // fixme is it OK to use NPoco InsertBulk here, or should we use our own BulkInsertRecords? + // fixme is we'd like to use the native NPoco InsertBulk here but it causes problems (not sure exaclty all scenarios) but by using SQL Server and updating a variants name will cause: Unable to cast object of type 'Umbraco.Core.Persistence.FaultHandling.RetryDbConnection' to type 'System.Data.SqlClient.SqlConnection'. // (same in PersistNewItem above) // insert content variations - Database.InsertBulk(GetContentVariationDtos(content, publishing)); + Database.BulkInsertRecords(GetContentVariationDtos(content, publishing)); // insert document variations - Database.InsertBulk(GetDocumentVariationDtos(content, publishing, editedCultures)); + Database.BulkInsertRecords(GetDocumentVariationDtos(content, publishing, editedCultures)); } // refresh content diff --git a/src/Umbraco.Core/Publishing/ScheduledPublisher.cs b/src/Umbraco.Core/Publishing/ScheduledPublisher.cs index bb09195691..b16cd961f0 100644 --- a/src/Umbraco.Core/Publishing/ScheduledPublisher.cs +++ b/src/Umbraco.Core/Publishing/ScheduledPublisher.cs @@ -39,7 +39,7 @@ namespace Umbraco.Core.Publishing try { d.ReleaseDate = null; - d.PublishValues(); // fixme variants? + d.TryPublishValues(); // fixme variants? var result = _contentService.SaveAndPublish(d, d.GetWriterProfile().Id); _logger.Debug($"Result of publish attempt: {result.Result}"); if (result.Success == false) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 84e616d4d7..fc37000e13 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -1070,7 +1070,7 @@ namespace Umbraco.Core.Services.Implement try { d.ReleaseDate = null; - d.PublishValues(); // fixme variants? + d.TryPublishValues(); // fixme variants? result = SaveAndPublish(d, d.WriterId); if (result.Success == false) Logger.Error($"Failed to publish document id={d.Id}, reason={result.Result}."); @@ -1110,7 +1110,7 @@ namespace Umbraco.Core.Services.Implement bool IsEditing(IContent c, string l, string s) => c.Properties.Any(x => x.Values.Where(y => y.Culture == l && y.Segment == s).Any(y => y.EditedValue != y.PublishedValue)); - return SaveAndPublishBranch(content, force, document => IsEditing(document, culture, segment), document => document.PublishValues(culture, segment), userId); + return SaveAndPublishBranch(content, force, document => IsEditing(document, culture, segment), document => document.TryPublishValues(culture, segment), userId); } /// diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 502178377e..c87774ba27 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -106,17 +106,17 @@ namespace Umbraco.Tests.Integration private IContent CreateBranch() { var content1 = MockedContent.CreateSimpleContent(_contentType, "Content1"); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); // 2 (published) // .1 (published) // .2 (not published) var content2 = MockedContent.CreateSimpleContent(_contentType, "Content2", content1); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content21 = MockedContent.CreateSimpleContent(_contentType, "Content21", content2); - content21.PublishValues(); + content21.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content21); var content22 = MockedContent.CreateSimpleContent(_contentType, "Content22", content2); ServiceContext.ContentService.Save(content22); @@ -135,12 +135,12 @@ namespace Umbraco.Tests.Integration // .1 (published) // .2 (not published) var content4 = MockedContent.CreateSimpleContent(_contentType, "Content4", content1); - content4.PublishValues(); + content4.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content4); content4.Name = "Content4X"; ServiceContext.ContentService.Save(content4); var content41 = MockedContent.CreateSimpleContent(_contentType, "Content41", content4); - content41.PublishValues(); + content41.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content41); var content42 = MockedContent.CreateSimpleContent(_contentType, "Content42", content4); ServiceContext.ContentService.Save(content42); @@ -149,10 +149,10 @@ namespace Umbraco.Tests.Integration // .1 (published) // .2 (not published) var content5 = MockedContent.CreateSimpleContent(_contentType, "Content5", content1); - content5.PublishValues(); + content5.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content5); var content51 = MockedContent.CreateSimpleContent(_contentType, "Content51", content5); - content51.PublishValues(); + content51.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content51); var content52 = MockedContent.CreateSimpleContent(_contentType, "Content52", content5); ServiceContext.ContentService.Save(content52); @@ -439,7 +439,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -476,7 +476,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -513,7 +513,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -553,7 +553,7 @@ namespace Umbraco.Tests.Integration ResetEvents(); content.Name = "changed"; - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); @@ -574,12 +574,12 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); content.Name = "changed"; - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); @@ -606,7 +606,7 @@ namespace Umbraco.Tests.Integration ResetEvents(); content.Name = "changed"; - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); @@ -630,12 +630,12 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); content.Name = "changed"; - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); @@ -656,7 +656,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -680,7 +680,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); content.Name = "changed"; ServiceContext.ContentService.Save(content); @@ -741,7 +741,7 @@ namespace Umbraco.Tests.Integration ServiceContext.ContentService.Unpublish(content1); ResetEvents(); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); Assert.AreEqual(2, _msgCount); @@ -984,7 +984,7 @@ namespace Umbraco.Tests.Integration var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -1008,7 +1008,7 @@ namespace Umbraco.Tests.Integration var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ServiceContext.ContentService.MoveToRecycleBin(content); @@ -1033,7 +1033,7 @@ namespace Umbraco.Tests.Integration var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); content.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content); @@ -1204,7 +1204,7 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -1224,7 +1224,7 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); content.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content); @@ -1246,11 +1246,11 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Unpublish(content1); @@ -1332,7 +1332,7 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); @@ -1354,7 +1354,7 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); content1.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content1); @@ -1380,7 +1380,7 @@ namespace Umbraco.Tests.Integration Assert.IsNotNull(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ResetEvents(); @@ -1402,11 +1402,11 @@ namespace Umbraco.Tests.Integration Assert.IsNotNull(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -1427,11 +1427,11 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ResetEvents(); @@ -1451,15 +1451,15 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -1480,13 +1480,13 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); content1.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ResetEvents(); @@ -1506,17 +1506,17 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); content1.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -1537,16 +1537,16 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ResetEvents(); @@ -1566,20 +1566,20 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); var content4 = CreateContent(content3.Id); Assert.IsNotNull(content4); - content4.PublishValues(); + content4.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content4); ServiceContext.ContentService.Unpublish(content3); @@ -1600,18 +1600,18 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); content2.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ResetEvents(); @@ -1631,22 +1631,22 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); content2.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); var content4 = CreateContent(content3.Id); Assert.IsNotNull(content4); - content4.PublishValues(); + content4.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content4); ServiceContext.ContentService.Unpublish(content3); @@ -1667,11 +1667,11 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); @@ -1694,11 +1694,11 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); content2.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content2); @@ -1778,7 +1778,7 @@ namespace Umbraco.Tests.Integration var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ResetEvents(); @@ -1832,11 +1832,11 @@ namespace Umbraco.Tests.Integration var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -1891,7 +1891,7 @@ namespace Umbraco.Tests.Integration var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Move(content1, content2.Id); @@ -2001,11 +2001,11 @@ namespace Umbraco.Tests.Integration var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -2081,7 +2081,7 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -2101,7 +2101,7 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); var content2 = CreateContent(); Assert.IsNotNull(content2); @@ -2124,7 +2124,7 @@ namespace Umbraco.Tests.Integration { var content = CreateBranch(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -2166,17 +2166,17 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); var v1 = content.VersionId; content.Properties.First().SetValue("changed"); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); var v2 = content.VersionId; content.Properties.First().SetValue("again"); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); var v3 = content.VersionId; @@ -2210,12 +2210,12 @@ namespace Umbraco.Tests.Integration Assert.IsFalse(content.IsPropertyDirty("Published")); Assert.IsFalse(content.WasPropertyDirty("Published")); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.IsFalse(content.IsPropertyDirty("Published")); Assert.IsTrue(content.WasPropertyDirty("Published")); // has just been published - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.IsFalse(content.IsPropertyDirty("Published")); Assert.IsFalse(content.WasPropertyDirty("Published")); // was published already diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 5bfd3f2de5..0bb1da5cc1 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -215,7 +215,7 @@ namespace Umbraco.Tests.Models // can publish value // and get edited and published values - content.PublishValues(); + content.TryPublishValues(); Assert.AreEqual("a", content.GetValue("prop")); Assert.AreEqual("a", content.GetValue("prop", published: true)); @@ -244,9 +244,9 @@ namespace Umbraco.Tests.Models // can publish value // and get edited and published values - Assert.Throws(() => content.PublishValues(langFr)); // no name + Assert.Throws(() => content.TryPublishValues(langFr)); // no name content.SetName(langFr, "name-fr"); - content.PublishValues(langFr); + content.TryPublishValues(langFr); Assert.AreEqual("b", content.GetValue("prop")); Assert.IsNull(content.GetValue("prop", published: true)); Assert.AreEqual("c", content.GetValue("prop", langFr)); @@ -260,7 +260,7 @@ namespace Umbraco.Tests.Models Assert.IsNull(content.GetValue("prop", langFr, published: true)); // can publish all - content.PublishAllValues(); + content.TryPublishAllValues(); Assert.AreEqual("b", content.GetValue("prop")); Assert.AreEqual("b", content.GetValue("prop", published: true)); Assert.AreEqual("c", content.GetValue("prop", langFr)); @@ -322,12 +322,12 @@ namespace Umbraco.Tests.Models content.SetValue("prop", "a-es", langEs); // cannot publish without a name - Assert.Throws(() => content.PublishValues(langFr)); + Assert.Throws(() => content.TryPublishValues(langFr)); // works with a name // and then FR is available, and published content.SetName(langFr, "name-fr"); - content.PublishValues(langFr); + content.TryPublishValues(langFr); // now UK is available too content.SetName(langUk, "name-uk"); diff --git a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs index 3bc419ea95..0afc581f83 100644 --- a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs +++ b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs @@ -187,13 +187,13 @@ namespace Umbraco.Tests.Persistence.NPocoTests contentTypeService.Save(contentType); var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); id2 = content1.Id; var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); id3 = content2.Id; diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index f01c59df03..caa2453aed 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -140,7 +140,7 @@ namespace Umbraco.Tests.Persistence.Repositories // publish = new edit version content1.SetValue("title", "title"); - ((Content) content1).PublishValues(); + ((Content) content1).TryPublishValues(); ((Content) content1).PublishedState = PublishedState.Publishing; repository.Save(content1); @@ -202,7 +202,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual(false, scope.Database.ExecuteScalar($"SELECT published FROM {Constants.DatabaseSchema.Tables.Document} WHERE nodeId=@id", new { id = content1.Id })); // publish = version - ((Content) content1).PublishValues(); + ((Content) content1).TryPublishValues(); ((Content) content1).PublishedState = PublishedState.Publishing; repository.Save(content1); @@ -238,7 +238,7 @@ namespace Umbraco.Tests.Persistence.Repositories // publish = new version content1.Name = "name-4"; content1.SetValue("title", "title-4"); - ((Content) content1).PublishValues(); + ((Content) content1).TryPublishValues(); ((Content) content1).PublishedState = PublishedState.Publishing; repository.Save(content1); @@ -648,7 +648,7 @@ namespace Umbraco.Tests.Persistence.Repositories // publish them all foreach (var content in result) { - content.PublishValues(); + content.TryPublishValues(); repository.Save(content); } diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index bb3b41d128..06c13b2733 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -146,7 +146,7 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); scope.Complete(); } @@ -161,7 +161,7 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { item.Name = "changed"; - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); if (complete) diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 258056f198..899d2f999e 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -123,10 +123,10 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); // should create an xml clone item.Name = "changed"; - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); // should re-use the xml clone // this should never change @@ -230,13 +230,13 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); for (var i = 0; i < count; i++) { var temp = new Content("content_" + i, -1, contentType); - temp.PublishValues(); + temp.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(temp); ids[i] = temp.Id; } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index c45d9be5f3..8bb5f1bc9e 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -182,16 +182,16 @@ namespace Umbraco.Tests.Services { var contentService = ServiceContext.ContentService; var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); - root.PublishValues(); + root.TryPublishValues(); Assert.IsTrue(contentService.SaveAndPublish(root).Success); var content = contentService.CreateAndSave("Test", -1, "umbTextpage", 0); - content.PublishValues(); + content.TryPublishValues(); Assert.IsTrue(contentService.SaveAndPublish(content).Success); var hierarchy = CreateContentHierarchy().OrderBy(x => x.Level).ToArray(); contentService.Save(hierarchy, 0); foreach (var c in hierarchy) { - c.PublishValues(); + c.TryPublishValues(); Assert.IsTrue(contentService.SaveAndPublish(c).Success); } @@ -241,7 +241,7 @@ namespace Umbraco.Tests.Services // Assert - content.PublishValues(); + content.TryPublishValues(); Assert.IsTrue(contentService.SaveAndPublish(content).Success); } @@ -256,7 +256,7 @@ namespace Umbraco.Tests.Services for (var i = 0; i < 20; i++) { content.SetValue("bodyText", "hello world " + Guid.NewGuid()); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); } @@ -395,12 +395,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "plus" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); // verify @@ -436,7 +436,7 @@ namespace Umbraco.Tests.Services allTags = tagService.GetAllContentTags(); Assert.AreEqual(4, allTags.Count()); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); Assert.IsTrue(content1.Published); @@ -466,12 +466,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "bam" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); // verify @@ -507,9 +507,9 @@ namespace Umbraco.Tests.Services allTags = tagService.GetAllContentTags(); Assert.AreEqual(0, allTags.Count()); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); // tags are back @@ -538,12 +538,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "plus" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", content1.Id); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); // verify @@ -578,7 +578,7 @@ namespace Umbraco.Tests.Services allTags = tagService.GetAllContentTags(); Assert.AreEqual(0, allTags.Count()); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); Assert.IsTrue(content1.Published); @@ -616,12 +616,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "bam" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); contentService.Unpublish(content1); @@ -637,7 +637,7 @@ namespace Umbraco.Tests.Services var allTags = tagService.GetAllContentTags(); Assert.AreEqual(0, allTags.Count()); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); tags = tagService.GetTagsForEntity(content2.Id); @@ -663,12 +663,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "bam" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", content1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); contentService.Unpublish(content1); @@ -684,7 +684,7 @@ namespace Umbraco.Tests.Services var allTags = tagService.GetAllContentTags(); Assert.AreEqual(0, allTags.Count()); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); tags = tagService.GetTagsForEntity(content2.Id); @@ -768,7 +768,7 @@ namespace Umbraco.Tests.Services // create a content with tags and publish var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1); content.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // edit tags and save @@ -808,7 +808,7 @@ namespace Umbraco.Tests.Services // Act content.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Assert @@ -839,12 +839,12 @@ namespace Umbraco.Tests.Services contentTypeService.Save(contentType); var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1); content.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Act content.AssignTags("tags", new[] { "another", "world" }, merge: true); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Assert @@ -875,12 +875,12 @@ namespace Umbraco.Tests.Services contentTypeService.Save(contentType); var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1); content.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Act content.RemoveTags("tags", new[] { "some", "world" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Assert @@ -1063,7 +1063,7 @@ namespace Umbraco.Tests.Services var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 2); Assert.IsFalse(parent.Published); - parent.PublishValues(); + parent.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(parent); // publishing parent, so Text Page 2 can be updated. var content = contentService.GetById(NodeDto.NodeIdSeed + 4); @@ -1076,7 +1076,7 @@ namespace Umbraco.Tests.Services content.Name = "Text Page 2 Updated"; content.SetValue("author", "Jane Doe"); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // publishes the current version, creates a version var version2 = content.VersionId; @@ -1084,7 +1084,7 @@ namespace Umbraco.Tests.Services content.Name = "Text Page 2 ReUpdated"; content.SetValue("author", "Bob Hope"); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // publishes again, creates a version var version3 = content.VersionId; @@ -1151,11 +1151,11 @@ namespace Umbraco.Tests.Services // Arrange var contentService = ServiceContext.ContentService; var root = contentService.GetById(NodeDto.NodeIdSeed + 2); - root.PublishValues(); + root.TryPublishValues(); contentService.SaveAndPublish(root); var content = contentService.GetById(NodeDto.NodeIdSeed + 4); content.ExpireDate = DateTime.Now.AddSeconds(1); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Act @@ -1205,7 +1205,7 @@ namespace Umbraco.Tests.Services // Arrange var contentService = ServiceContext.ContentService; var content = contentService.GetById(NodeDto.NodeIdSeed + 2); - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); using (var scope = ScopeProvider.CreateScope()) @@ -1235,7 +1235,7 @@ namespace Umbraco.Tests.Services var content = contentService.GetById(NodeDto.NodeIdSeed + 2); // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1249,7 +1249,7 @@ namespace Umbraco.Tests.Services // Arrange var contentService = ServiceContext.ContentService; var parent = contentService.Create("parent", -1, "umbTextpage"); - parent.PublishValues(); + parent.TryPublishValues(); contentService.SaveAndPublish(parent); var content = contentService.Create("child", parent, "umbTextpage"); contentService.Save(content); @@ -1275,7 +1275,7 @@ namespace Umbraco.Tests.Services Assert.AreEqual("Home", content.Name); content.Name = "foo"; - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); Assert.That(published.Success, Is.True); @@ -1316,7 +1316,7 @@ namespace Umbraco.Tests.Services var parent = contentService.GetById(parentId); - var parentCanPublishValues = parent.PublishValues(); + var parentCanPublishValues = parent.TryPublishValues(); var parentPublished = contentService.SaveAndPublish(parent); // parent can publish values @@ -1325,7 +1325,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(parentPublished.Success); Assert.IsTrue(parent.Published); - var contentCanPublishValues = content.PublishValues(); + var contentCanPublishValues = content.TryPublishValues(); var contentPublished = contentService.SaveAndPublish(content); // content cannot publish values because they are invalid @@ -1401,11 +1401,11 @@ namespace Umbraco.Tests.Services contentService.Save(content); var parent = contentService.GetById(NodeDto.NodeIdSeed + 2); - parent.PublishValues(); + parent.TryPublishValues(); var parentPublished = contentService.SaveAndPublish(parent, 0);//Publish root Home node to enable publishing of 'NodeDto.NodeIdSeed + 3' // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1424,11 +1424,11 @@ namespace Umbraco.Tests.Services contentService.Save(content, 0); var parent = contentService.GetById(NodeDto.NodeIdSeed + 2); - parent.PublishValues(); + parent.TryPublishValues(); var parentPublished = contentService.SaveAndPublish(parent, 0);//Publish root Home node to enable publishing of 'NodeDto.NodeIdSeed + 3' // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1461,7 +1461,7 @@ namespace Umbraco.Tests.Services var content = contentService.GetById(NodeDto.NodeIdSeed + 5); // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1479,7 +1479,7 @@ namespace Umbraco.Tests.Services content.SetValue("author", "Barack Obama"); // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1504,14 +1504,14 @@ namespace Umbraco.Tests.Services content.SetValue("author", "Barack Obama"); // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); var childContent = contentService.Create("Child", content.Id, "umbTextpage", 0); // Reset all identity properties childContent.Id = 0; childContent.Path = null; ((Content)childContent).ResetIdentity(); - childContent.PublishValues(); + childContent.TryPublishValues(); var childPublished = contentService.SaveAndPublish(childContent, 0); // Assert @@ -1530,12 +1530,12 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var root = contentService.GetById(NodeDto.NodeIdSeed + 2); - root.PublishValues(); + root.TryPublishValues(); var rootPublished = contentService.SaveAndPublish(root); var content = contentService.GetById(NodeDto.NodeIdSeed + 4); content.Properties["title"].SetValue(content.Properties["title"].GetValue() + " Published"); - content.PublishValues(); + content.TryPublishValues(); var contentPublished = contentService.SaveAndPublish(content); var publishedVersion = content.VersionId; @@ -1835,13 +1835,13 @@ namespace Umbraco.Tests.Services content1.PropertyValues(obj); content1.ResetDirtyProperties(false); ServiceContext.ContentService.Save(content1, 0); - content1.PublishValues(); + content1.TryPublishValues(); Assert.IsTrue(ServiceContext.ContentService.SaveAndPublish(content1, 0).Success); var content2 = MockedContent.CreateBasicContent(contentType); content2.PropertyValues(obj); content2.ResetDirtyProperties(false); ServiceContext.ContentService.Save(content2, 0); - content2.PublishValues(); + content2.TryPublishValues(); Assert.IsTrue(ServiceContext.ContentService.SaveAndPublish(content2, 0).Success); var editorGroup = ServiceContext.UserService.GetUserGroupByAlias("editor"); @@ -1999,7 +1999,7 @@ namespace Umbraco.Tests.Services Assert.AreEqual(0, contentTags.Length); // publish - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // now tags have been set (published) @@ -2016,7 +2016,7 @@ namespace Umbraco.Tests.Services Assert.AreEqual(0, copiedTags.Length); // publish - copy.PublishValues(); + copy.TryPublishValues(); contentService.SaveAndPublish(copy); // now tags have been set (published) @@ -2035,7 +2035,7 @@ namespace Umbraco.Tests.Services var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 2); Assert.IsFalse(parent.Published); - parent.PublishValues(); + parent.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(parent); // publishing parent, so Text Page 2 can be updated. var content = contentService.GetById(NodeDto.NodeIdSeed + 4); @@ -2052,7 +2052,7 @@ namespace Umbraco.Tests.Services // non published = edited Assert.IsTrue(content.Edited); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // new version var version2 = content.VersionId; Assert.AreNotEqual(version1, version2); @@ -2077,7 +2077,7 @@ namespace Umbraco.Tests.Services content.Name = "Text Page 2 ReReUpdated"; - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // new version var version3 = content.VersionId; Assert.AreNotEqual(version2, version3); @@ -2133,7 +2133,7 @@ namespace Umbraco.Tests.Services content = contentService.GetById(content.Id); Assert.AreEqual("Text Page 2 ReReUpdated", content.Name); Assert.AreEqual("Jane Doe", content.GetValue("author")); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); Assert.IsFalse(content.Edited); content.Name = "Xxx"; @@ -2251,7 +2251,7 @@ namespace Umbraco.Tests.Services Assert.IsFalse(scope.Database.Exists(content.Id)); } - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); using (var scope = ScopeProvider.CreateScope()) @@ -2393,7 +2393,7 @@ namespace Umbraco.Tests.Services // becomes Published, !Edited // creates a new version // can get published property values - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); Assert.IsTrue(content.Published); @@ -2548,8 +2548,8 @@ namespace Umbraco.Tests.Services // act - content.PublishValues(langFr.IsoCode); - content.PublishValues(langUk.IsoCode); + content.TryPublishValues(langFr.IsoCode); + content.TryPublishValues(langUk.IsoCode); contentService.SaveAndPublish(content); // both FR and UK have been published, @@ -2591,7 +2591,7 @@ namespace Umbraco.Tests.Services // act - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // now it has publish name for invariant neutral @@ -2780,7 +2780,7 @@ namespace Umbraco.Tests.Services // act - content.PublishValues(langUk.IsoCode); + content.TryPublishValues(langUk.IsoCode); contentService.SaveAndPublish(content); content2 = contentService.GetById(content.Id); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 1944516164..c9f8ba52ad 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -88,7 +88,7 @@ namespace Umbraco.Tests.Services var contentType = contentTypes[index]; var contentItem = MockedContent.CreateSimpleContent(contentType, "MyName_" + index + "_" + i, parentId); ServiceContext.ContentService.Save(contentItem); - contentItem.PublishValues(); + contentItem.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(contentItem); parentId = contentItem.Id; @@ -189,7 +189,7 @@ namespace Umbraco.Tests.Services var contentType = contentTypes[index]; var contentItem = MockedContent.CreateSimpleContent(contentType, "MyName_" + index + "_" + i, parentId); ServiceContext.ContentService.Save(contentItem); - contentItem.PublishValues(); + contentItem.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(contentItem); parentId = contentItem.Id; } @@ -225,19 +225,19 @@ namespace Umbraco.Tests.Services var root = MockedContent.CreateSimpleContent(contentType1, "Root", -1); ServiceContext.ContentService.Save(root); - root.PublishValues(); + root.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(root); var level1 = MockedContent.CreateSimpleContent(contentType2, "L1", root.Id); ServiceContext.ContentService.Save(level1); - level1.PublishValues(); + level1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(level1); for (int i = 0; i < 2; i++) { var level3 = MockedContent.CreateSimpleContent(contentType3, "L2" + i, level1.Id); ServiceContext.ContentService.Save(level3); - level3.PublishValues(); + level3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(level3); } @@ -267,7 +267,7 @@ namespace Umbraco.Tests.Services ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); IContent contentItem = MockedContent.CreateTextpageContent(contentType1, "Testing", -1); - contentItem.PublishValues(); + contentItem.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(contentItem); var initProps = contentItem.Properties.Count; var initPropTypes = contentItem.PropertyTypes.Count(); @@ -297,14 +297,14 @@ namespace Umbraco.Tests.Services var contentItems1 = MockedContent.CreateTextpageContent(contentType1, -1, 10).ToArray(); foreach (var x in contentItems1) { - x.PublishValues(); + x.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(x); } var contentItems2 = MockedContent.CreateTextpageContent(contentType2, -1, 5).ToArray(); foreach (var x in contentItems2) { - x.PublishValues(); + x.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(x); } @@ -362,7 +362,7 @@ namespace Umbraco.Tests.Services var contentItems1 = MockedContent.CreateTextpageContent(contentType1, -1, 10).ToArray(); foreach (var x in contentItems1) { - x.PublishValues(); + x.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(x); } var alias = contentType1.PropertyTypes.First().Alias; @@ -496,7 +496,7 @@ namespace Umbraco.Tests.Services // Act var homeDoc = cs.Create("Home Page", -1, contentTypeAlias); - homeDoc.PublishValues(); + homeDoc.TryPublishValues(); cs.SaveAndPublish(homeDoc); // Assert diff --git a/src/Umbraco.Tests/Services/PerformanceTests.cs b/src/Umbraco.Tests/Services/PerformanceTests.cs index b157eb669d..dd84c49ef0 100644 --- a/src/Umbraco.Tests/Services/PerformanceTests.cs +++ b/src/Umbraco.Tests/Services/PerformanceTests.cs @@ -249,7 +249,7 @@ namespace Umbraco.Tests.Services var result = new List(); ServiceContext.ContentTypeService.Save(contentType1); IContent lastParent = MockedContent.CreateSimpleContent(contentType1); - lastParent.PublishValues(); + lastParent.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(lastParent); result.Add(lastParent); //create 20 deep @@ -263,7 +263,7 @@ namespace Umbraco.Tests.Services //only publish evens if (j % 2 == 0) { - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); } else diff --git a/src/Umbraco.Tests/Services/TagServiceTests.cs b/src/Umbraco.Tests/Services/TagServiceTests.cs index 0db27e9f4c..c24c729bf8 100644 --- a/src/Umbraco.Tests/Services/TagServiceTests.cs +++ b/src/Umbraco.Tests/Services/TagServiceTests.cs @@ -36,21 +36,21 @@ namespace Umbraco.Tests.Services IContent content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "cow", "pig", "goat" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); // change content1.AssignTags("tags", new[] { "elephant" }, true); content1.RemoveTags("tags", new[] { "cow" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); // more changes content1.AssignTags("tags", new[] { "mouse" }, true); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); content1.RemoveTags("tags", new[] { "mouse" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); // get it back @@ -88,17 +88,17 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "cow", "pig", "goat" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "cow", "pig" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); var content3 = MockedContent.CreateSimpleContent(contentType, "Tagged content 3", -1); content3.AssignTags("tags", new[] { "cow" }); - content3.PublishValues(); + content3.TryPublishValues(); contentService.SaveAndPublish(content3); // Act diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 6ce5f049e8..3612ee8847 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -643,22 +643,22 @@ namespace Umbraco.Web.Editors else { //publish the item and check if it worked, if not we will show a diff msg below - contentItem.PersistedContent.PublishValues(GetLanguageCulture(contentItem.LanguageId)); //we are not checking for a return value here because we've alraedy pre-validated the property values + contentItem.PersistedContent.TryPublishValues(GetLanguageCulture(contentItem.LanguageId)); //we are not checking for a return value here because we've already pre-validated the property values //check if we are publishing other variants and validate them - var allLangs = Services.LocalizationService.GetAllLanguages().ToList(); + var allLangs = Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x); var variantsToValidate = contentItem.PublishVariations.Where(x => x.LanguageId != contentItem.LanguageId).ToList(); foreach (var publishVariation in variantsToValidate) { - if (!contentItem.PersistedContent.PublishValues(GetLanguageCulture(publishVariation.LanguageId))) + if (!contentItem.PersistedContent.TryPublishValues(GetLanguageCulture(publishVariation.LanguageId))) { - var errMsg = Services.TextService.Localize("speechBubbles/contentLangValidationError", new[]{allLangs.First(x => x.Id == publishVariation.LanguageId).CultureName}); + var errMsg = Services.TextService.Localize("speechBubbles/contentLangValidationError", new[] {allLangs[publishVariation.LanguageId].CultureName}); ModelState.AddModelError("publish_variant_" + publishVariation.LanguageId + "_", errMsg); } } //validate any mandatory variants that are not in the list - var mandatoryLangs = Mapper.Map, IEnumerable>(allLangs) + var mandatoryLangs = Mapper.Map, IEnumerable>(allLangs.Values) .Where(x => variantsToValidate.All(v => v.LanguageId != x.Id)) //don't include variants above .Where(x => x.Id != contentItem.LanguageId) //don't include the current variant .Where(x => x.Mandatory); @@ -666,7 +666,7 @@ namespace Umbraco.Web.Editors { if (contentItem.PersistedContent.Validate(GetLanguageCulture(lang.Id)).Length > 0) { - var errMsg = Services.TextService.Localize("speechBubbles/contentReqLangValidationError", new[]{allLangs.First(x => x.Id == lang.Id).CultureName}); + var errMsg = Services.TextService.Localize("speechBubbles/contentReqLangValidationError", new[]{allLangs[lang.Id].CultureName}); ModelState.AddModelError("publish_variant_" + lang.Id + "_", errMsg); } } @@ -749,7 +749,7 @@ namespace Umbraco.Web.Editors return HandleContentNotFound(id, false); } - foundContent.PublishValues(); // fixme variants? + foundContent.TryPublishValues(); // fixme variants? var publishResult = Services.ContentService.SaveAndPublish(foundContent, Security.GetUserId().ResultOr(0)); if (publishResult.Success == false) { diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs index 41383764bb..2e8155e1a7 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs @@ -1,4 +1,5 @@ using AutoMapper; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; using ContentVariation = Umbraco.Core.Models.ContentVariation; @@ -13,7 +14,12 @@ namespace Umbraco.Web.Models.Mapping public string Resolve(IContent source, ContentItemDisplay destination, string destMember, ResolutionContext context) { var culture = context.GetCulture(); - return source.GetName(culture); + if (culture != null && source.ContentType.Variations.Has(ContentVariation.CultureNeutral)) + { + //return the culture name being requested + return source.GetName(culture); + } + return source.Name; } } } diff --git a/src/Umbraco.Web/WebServices/BulkPublishController.cs b/src/Umbraco.Web/WebServices/BulkPublishController.cs index daea5cc58f..714c1ac0db 100644 --- a/src/Umbraco.Web/WebServices/BulkPublishController.cs +++ b/src/Umbraco.Web/WebServices/BulkPublishController.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.WebServices if (publishDescendants == false) { - content.PublishValues(); // fixme variants? validation - when this returns null? + content.TryPublishValues(); // fixme variants? validation - when this returns null? var result = Services.ContentService.SaveAndPublish(content, Security.CurrentUser.Id); return Json(new { From ffd85b4e94ee8b0f6ee8611d06a91280a73af528 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 26 Apr 2018 22:54:36 +1000 Subject: [PATCH 39/97] Fixes more bugs and outbound routing for additional urls --- .../Integration/GetCultureTests.cs | 120 ------------------ .../Routing/DomainsAndCulturesTests.cs | 44 +------ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 - src/Umbraco.Web/Models/ContentExtensions.cs | 107 ---------------- .../NuCache/PublishedContent.cs | 2 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 91 ++++++++++--- src/Umbraco.Web/Routing/DomainHelper.cs | 3 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 - src/Umbraco.Web/umbraco.presentation/page.cs | 2 +- 9 files changed, 75 insertions(+), 296 deletions(-) delete mode 100644 src/Umbraco.Tests/Integration/GetCultureTests.cs delete mode 100644 src/Umbraco.Web/Models/ContentExtensions.cs diff --git a/src/Umbraco.Tests/Integration/GetCultureTests.cs b/src/Umbraco.Tests/Integration/GetCultureTests.cs deleted file mode 100644 index ec3a73b37e..0000000000 --- a/src/Umbraco.Tests/Integration/GetCultureTests.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Tests.Services; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Tests.Testing; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Integration -{ - [TestFixture] - [Apartment(ApartmentState.STA)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class GetCultureTests : TestWithSomeContentBase - { - protected override void Compose() - { - base.Compose(); - Container.Register(); - } - - [Test] - public void GetCulture() - { - var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type"); - contentTypeService.Save(contentType); - var contentService = ServiceContext.ContentService; - - var c1 = contentService.CreateAndSave("content", -1, "umbBlah"); - var c2 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c3 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c4 = contentService.CreateAndSave("content", c3, "umbBlah"); - - foreach (var l in ServiceContext.LocalizationService.GetAllLanguages().Where(x => x.CultureName != "en-US").ToArray()) - ServiceContext.LocalizationService.Delete(l); - - var l0 = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); - var l1 = new Core.Models.Language("fr-FR"); - var l2 = new Core.Models.Language("de-DE"); - ServiceContext.LocalizationService.Save(l1); - ServiceContext.LocalizationService.Save(l2); - - foreach (var d in ServiceContext.DomainService.GetAll(true).ToArray()) - ServiceContext.DomainService.Delete(d); - - ServiceContext.DomainService.Save(new UmbracoDomain("domain1.com") {DomainName="domain1.com", RootContentId = c1.Id, LanguageId = l0.Id}); - ServiceContext.DomainService.Save(new UmbracoDomain("domain1.fr") { DomainName = "domain1.fr", RootContentId = c1.Id, LanguageId = l1.Id }); - ServiceContext.DomainService.Save(new UmbracoDomain("*100112") { DomainName = "*100112", RootContentId = c3.Id, LanguageId = l2.Id }); - - var content = c2; - var culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.com/")); - Assert.AreEqual("en-US", culture.Name); - - content = c2; - culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.fr/")); - Assert.AreEqual("fr-FR", culture.Name); - - content = c4; - culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.fr/")); - Assert.AreEqual("de-DE", culture.Name); - } - - [Test] - public void GetCultureWithWildcard() - { - var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type"); - contentTypeService.Save(contentType); - var contentService = ServiceContext.ContentService; - - var c1 = contentService.CreateAndSave("content", -1, "umbBlah"); - var c2 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c3 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c4 = contentService.CreateAndSave("content", c3, "umbBlah"); - - foreach (var l in ServiceContext.LocalizationService.GetAllLanguages().Where(x => x.CultureName != "en-US").ToArray()) - ServiceContext.LocalizationService.Delete(l); - - var l0 = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); - var l1 = new Core.Models.Language("fr-FR"); - var l2 = new Core.Models.Language("de-DE"); - ServiceContext.LocalizationService.Save(l1); - ServiceContext.LocalizationService.Save(l2); - - foreach (var d in ServiceContext.DomainService.GetAll(true).ToArray()) - ServiceContext.DomainService.Delete(d); - - ServiceContext.DomainService.Save(new UmbracoDomain("*0000") { DomainName = "*0000", RootContentId = c1.Id, LanguageId = l2.Id }); - ServiceContext.DomainService.Save(new UmbracoDomain("*0001") { DomainName = "*0001", RootContentId = c3.Id, LanguageId = l1.Id }); - //ServiceContext.DomainService.Save(new UmbracoDomain("*100112") { DomainName = "*100112", RootContentId = c3.Id, LanguageId = l2.Id }); - - var content = c2; - var culture = Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.com/")); - Assert.AreEqual("de-DE", culture.Name); - - content = c4; - culture = Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.fr/")); - Assert.AreEqual("fr-FR", culture.Name); - } - } -} diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index 1740f1288c..e4fcfc46f8 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -338,48 +338,6 @@ namespace Umbraco.Tests.Routing } - - #region Cases - [TestCase(10011, "http://domain1.com/", "en-US")] - [TestCase(100111, "http://domain1.com/", "en-US")] - [TestCase(10011, "http://domain1.fr/", "fr-FR")] - [TestCase(100111, "http://domain1.fr/", "fr-FR")] - [TestCase(1001121, "http://domain1.fr/", "de-DE")] - #endregion - public void GetCulture(int nodeId, string currentUrl, string expectedCulture) - { - var domainService = SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") - { - Id = 1, - LanguageId = LangEngId, - RootContentId = 1001, - LanguageIsoCode = "en-US" - }, - new UmbracoDomain("domain1.fr/") - { - Id = 1, - LanguageId = LangFrId, - RootContentId = 1001, - LanguageIsoCode = "fr-FR" - }, - new UmbracoDomain("*100112") - { - Id = 1, - LanguageId = LangDeId, - RootContentId = 100112, - LanguageIsoCode = "de-DE" - } - }); - - var umbracoContext = GetUmbracoContext("http://anything/"); - - var content = umbracoContext.ContentCache.GetById(nodeId); - Assert.IsNotNull(content); - - var culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(umbracoContext, domainService, ServiceContext.LocalizationService, null, new SiteDomainHelper(), content.Id, content.Path, new Uri(currentUrl)); - Assert.AreEqual(expectedCulture, culture.Name); - } + } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 3da04b48ca..8ae1e4dede 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -232,7 +232,6 @@ - diff --git a/src/Umbraco.Web/Models/ContentExtensions.cs b/src/Umbraco.Web/Models/ContentExtensions.cs deleted file mode 100644 index d6700769a9..0000000000 --- a/src/Umbraco.Web/Models/ContentExtensions.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; -using Umbraco.Web.Routing; -using Domain = Umbraco.Web.Routing.Domain; - -namespace Umbraco.Web.Models -{ - public static class ContentExtensions - { - //TODO: Not used - ///// - ///// Gets the culture that would be selected to render a specified content, - ///// within the context of a specified current request. - ///// - ///// The content. - ///// The request Uri. - ///// The culture that would be selected to render the content. - //public static CultureInfo GetCulture(this IContent content, Uri current = null) - //{ - // return GetCulture(UmbracoContext.Current, - // Current.Services.DomainService, - // Current.Services.LocalizationService, - // Current.Services.ContentService, - // content.Id, content.Path, - // current); - //} - - - - //TODO: Not used - only in tests - /// - /// Gets the culture that would be selected to render a specified content, - /// within the context of a specified current request. - /// - /// An instance. - /// An implementation. - /// An implementation. - /// An implementation. - /// The content identifier. - /// The content path. - /// The request Uri. - /// The culture that would be selected to render the content. - internal static CultureInfo GetCulture(UmbracoContext umbracoContext, - IDomainService domainService, ILocalizationService localizationService, IContentService contentService, - ISiteDomainHelper siteDomainHelper, - int contentId, string contentPath, Uri current) - { - var route = umbracoContext == null - ? null // for tests only - : umbracoContext.ContentCache.GetRouteById(contentId); // may be cached - - var domainCache = umbracoContext == null - ? new PublishedCache.XmlPublishedCache.DomainCache(domainService, localizationService) // for tests only - : umbracoContext.PublishedShapshot.Domains; // default - var domainHelper = umbracoContext.GetDomainHelper(siteDomainHelper); - Domain domain; - - if (route == null) - { - // if content is not published then route is null and we have to work - // on non-published content (note: could optimize by checking routes?) - - // fixme - even non-published content is stored in the cache or in the cmsContentNu table which would be faster to lookup - - var content = contentService.GetById(contentId); - if (content == null) - return GetDefaultCulture(localizationService); - - var hasDomain = domainHelper.NodeHasDomains(content.Id); - while (hasDomain == false && content != null) - { - content = content.Parent(contentService); - hasDomain = content != null && domainHelper.NodeHasDomains(content.Id); - } - - domain = hasDomain ? domainHelper.DomainForNode(content.Id, current) : null; - } - else - { - // if content is published then we have a route - // from which we can figure out the domain - - var pos = route.IndexOf('/'); - domain = pos == 0 - ? null - : domainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current); - } - - var rootContentId = domain == null ? -1 : domain.ContentId; - var wcDomain = DomainHelper.FindWildcardDomainInPath(domainCache.GetAll(true), contentPath, rootContentId); - - if (wcDomain != null) return wcDomain.Culture; - if (domain != null) return domain.Culture; - return GetDefaultCulture(localizationService); - } - - private static CultureInfo GetDefaultCulture(ILocalizationService localizationService) - { - var defaultLanguage = localizationService.GetDefaultVariantLanguage(); - return defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode); - } - } -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 883a43a7d5..480c18201f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -170,7 +170,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_cultureNames == null) { - var d = new Dictionary(); + var d = new Dictionary(StringComparer.InvariantCultureIgnoreCase); foreach(var c in _contentData.CultureInfos) { d[c.Key] = new PublishedCultureName(c.Value.Name, c.Value.Name.ToUrlSegment()); diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index d2f0c2662b..21a4b5c734 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; - +using Umbraco.Core.Models; + namespace Umbraco.Web.Routing { /// @@ -90,32 +92,79 @@ namespace Umbraco.Web.Routing /// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...). /// public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) - { - // will not use cache if previewing - var route = umbracoContext.ContentCache.GetRouteById(id); - - if (string.IsNullOrWhiteSpace(route)) - { - _logger.Debug(() => - $"Couldn't find any page with nodeId={id}. This is most likely caused by the page not being published."); - return null; + { + var node = umbracoContext.ContentCache.GetById(id); + var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); + + //if this is invariant, continue as we used to + if (!node.ContentType.Variations.Has(ContentVariation.CultureNeutral)) + { + // will not use cache if previewing + var route = umbracoContext.ContentCache.GetRouteById(id); + + return GetOtherUrlsForSinglePath(route, id, domainHelper, current); + } + else + { + //this is variant, so we need to find the domains and use the cultures assigned to get the route/paths + + var n = node; + var domainUris = domainHelper.DomainsForNode(n.Id, current, false); + while (domainUris == null && n != null) // n is null at root + { + // move to parent node + n = n.Parent; + domainUris = n == null ? null : domainHelper.DomainsForNode(n.Id, current); + } + + if (domainUris == null) + { + //we can't continue, there are no domains assigned but this is a culture variant node + return Enumerable.Empty(); + } + + var result = new List(); + foreach(var d in domainUris) + { + var route = umbracoContext.ContentCache.GetRouteById(id, d.Culture); + if (route == null) continue; + + //need to strip off the leading ID for the route + //TODO: Is there a nicer way to deal with this? + var pos = route.IndexOf('/'); + var path = pos == 0 ? route : route.Substring(pos); + + var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path)); + uri = UriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings); + result.Add(uri.ToString()); + } + return result; } - - var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); - - // extract domainUri and path - // route is / or / - var pos = route.IndexOf('/'); - var path = pos == 0 ? route : route.Substring(pos); - var domainUris = pos == 0 ? null : domainHelper.DomainsForNode(int.Parse(route.Substring(0, pos)), current); - - // assemble the alternate urls from domainUris (maybe empty) and path - return AssembleUrls(domainUris, path).Select(uri => uri.ToString()); + } #endregion #region Utilities + + private IEnumerable GetOtherUrlsForSinglePath(string route, int id, DomainHelper domainHelper, Uri current) + { + if (string.IsNullOrWhiteSpace(route)) + { + _logger.Debug(() => + $"Couldn't find any page with nodeId={id}. This is most likely caused by the page not being published."); + return null; + } + + // extract domainUri and path + // route is / or / + var pos = route.IndexOf('/'); + var path = pos == 0 ? route : route.Substring(pos); + var domainUris = pos == 0 ? null : domainHelper.DomainsForNode(int.Parse(route.Substring(0, pos)), current); + + // assemble the alternate urls from domainUris (maybe empty) and path + return AssembleUrls(domainUris, path).Select(uri => uri.ToString()); + } Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlProviderMode mode) { diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index d56473f530..ba7b59323b 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -128,7 +128,8 @@ namespace Umbraco.Web.Routing { //get the default domain (there should be one) domainAndUri = domainsAndUris.FirstOrDefault(x => x.IsDefault); - if (domainAndUri == null) domainsAndUris.First(); // take the first one by default (what else can we do?) + if (domainAndUri == null) + domainAndUri = domainsAndUris.First(); // take the first one by default (what else can we do?) } else { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 98afb387d4..44bc3ec324 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -567,7 +567,6 @@ - diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index 274ea83214..89ac52949f 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -481,7 +481,7 @@ namespace umbraco if (_cultureNames == null) { - var d = new Dictionary(); + var d = new Dictionary(StringComparer.InvariantCultureIgnoreCase); foreach (var c in _inner.Names) { d[c.Key] = new PublishedCultureName(c.Value, c.Value.ToUrlSegment()); From cb3cba5b053aa364a395ead8711089c8cf7474e1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 26 Apr 2018 23:34:06 +1000 Subject: [PATCH 40/97] Updates DefaultUrlProvider so that hieararchy of nodes that are variant and invariant still generate URLs correctly --- .../services/umbdataformatter.service.js | 30 +++--- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 101 +++++------------- 2 files changed, 44 insertions(+), 87 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index 9608fe20fb..5ef6eae293 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -326,19 +326,23 @@ //get the selected variant and build the additional published variants saveModel.publishVariations = []; - _.each(displayModel.variants, - function (d) { - //set the selected variant if this is current - if (d.current === true) { - saveModel.languageId = d.language.id; - } - if (d.publish === true) { - saveModel.publishVariations.push({ - languageId: d.language.id, - segment: d.segment - }); - } - }); + + //if there's more than 1 variant than we need to set the language and include the variants to publish + if (displayModel.variants.length > 1) { + _.each(displayModel.variants, + function (d) { + //set the selected variant if this is current + if (d.current === true) { + saveModel.languageId = d.language.id; + } + if (d.publish === true) { + saveModel.publishVariations.push({ + languageId: d.language.id, + segment: d.segment + }); + } + }); + } var propExpireDate = displayModel.removeDate; var propReleaseDate = displayModel.releaseDate; diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 21a4b5c734..c451d91565 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -7,6 +7,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing { @@ -96,75 +97,43 @@ namespace Umbraco.Web.Routing var node = umbracoContext.ContentCache.GetById(id); var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); - //if this is invariant, continue as we used to - if (!node.ContentType.Variations.Has(ContentVariation.CultureNeutral)) + var n = node; + var domainUris = domainHelper.DomainsForNode(n.Id, current, false); + while (domainUris == null && n != null) // n is null at root { - // will not use cache if previewing - var route = umbracoContext.ContentCache.GetRouteById(id); - - return GetOtherUrlsForSinglePath(route, id, domainHelper, current); + // move to parent node + n = n.Parent; + domainUris = n == null ? null : domainHelper.DomainsForNode(n.Id, current); } - else + + if (domainUris == null) { - //this is variant, so we need to find the domains and use the cultures assigned to get the route/paths + //there are no domains, exit + return Enumerable.Empty(); + } - var n = node; - var domainUris = domainHelper.DomainsForNode(n.Id, current, false); - while (domainUris == null && n != null) // n is null at root - { - // move to parent node - n = n.Parent; - domainUris = n == null ? null : domainHelper.DomainsForNode(n.Id, current); - } + var result = new List(); + foreach (var d in domainUris) + { + //although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok + var route = umbracoContext.ContentCache.GetRouteById(id, d.Culture); + if (route == null) continue; - if (domainUris == null) - { - //we can't continue, there are no domains assigned but this is a culture variant node - return Enumerable.Empty(); - } + //need to strip off the leading ID for the route + //TODO: Is there a nicer way to deal with this? + var pos = route.IndexOf('/'); + var path = pos == 0 ? route : route.Substring(pos); - var result = new List(); - foreach(var d in domainUris) - { - var route = umbracoContext.ContentCache.GetRouteById(id, d.Culture); - if (route == null) continue; - - //need to strip off the leading ID for the route - //TODO: Is there a nicer way to deal with this? - var pos = route.IndexOf('/'); - var path = pos == 0 ? route : route.Substring(pos); - - var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path)); - uri = UriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings); - result.Add(uri.ToString()); - } - return result; - } - + var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path)); + uri = UriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings); + result.Add(uri.ToString()); + } + return result; } #endregion #region Utilities - - private IEnumerable GetOtherUrlsForSinglePath(string route, int id, DomainHelper domainHelper, Uri current) - { - if (string.IsNullOrWhiteSpace(route)) - { - _logger.Debug(() => - $"Couldn't find any page with nodeId={id}. This is most likely caused by the page not being published."); - return null; - } - - // extract domainUri and path - // route is / or / - var pos = route.IndexOf('/'); - var path = pos == 0 ? route : route.Substring(pos); - var domainUris = pos == 0 ? null : domainHelper.DomainsForNode(int.Parse(route.Substring(0, pos)), current); - - // assemble the alternate urls from domainUris (maybe empty) and path - return AssembleUrls(domainUris, path).Select(uri => uri.ToString()); - } Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlProviderMode mode) { @@ -231,22 +200,6 @@ namespace Umbraco.Web.Routing return path == "/" ? path : path.TrimEnd('/'); } - // always build absolute urls unless we really cannot - IEnumerable AssembleUrls(IEnumerable domainUris, string path) - { - // no domain == no "other" url - if (domainUris == null) - return Enumerable.Empty(); - - // if no domain was found and then we have no "other" url - // else return absolute urls, ignoring vdir at that point - var uris = domainUris.Select(domainUri => new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path))); - - // UriFromUmbraco will handle vdir - // meaning it will add vdir into domain urls too! - return uris.Select(x => UriUtility.UriFromUmbraco(x, _globalSettings, _requestSettings)); - } - #endregion } } From 12d56cd201075fc7a66fbb713ea555d98a406d54 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 26 Apr 2018 23:48:07 +1000 Subject: [PATCH 41/97] fixes null check which fixes a whole lot of failing tests --- src/Umbraco.Core/Models/DomainExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Core/Models/DomainExtensions.cs b/src/Umbraco.Core/Models/DomainExtensions.cs index 47cd34105b..616685b6b9 100644 --- a/src/Umbraco.Core/Models/DomainExtensions.cs +++ b/src/Umbraco.Core/Models/DomainExtensions.cs @@ -13,6 +13,9 @@ namespace Umbraco.Core.Models public static bool IsDefaultDomain(this IDomain domain, ILocalizationService localizationService) { var defaultLang = localizationService.GetDefaultVariantLanguage(); + if (defaultLang == null) + return false; //if for some reason a null value is returned (i.e. no languages or based on mock unit test data), then assume false + return domain.LanguageIsoCode == defaultLang.CultureName; } } From 270f3a75ffe88e39781021bbf41e542a7e29f0aa Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Apr 2018 09:57:04 +1000 Subject: [PATCH 42/97] Fixes more tests --- src/Umbraco.Core/Models/Content.cs | 2 +- src/Umbraco.Tests/Models/VariationTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 708f0ac853..6b8732e70c 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -271,7 +271,7 @@ namespace Umbraco.Core.Models } /// - public IEnumerable PublishedCultures => _publishInfos.Keys; + public IEnumerable PublishedCultures => _publishInfos?.Keys; /// public bool IsCultureEdited(string culture) diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 0bb1da5cc1..bc0891e397 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -244,7 +244,7 @@ namespace Umbraco.Tests.Models // can publish value // and get edited and published values - Assert.Throws(() => content.TryPublishValues(langFr)); // no name + Assert.IsFalse(content.TryPublishValues(langFr)); // no name content.SetName(langFr, "name-fr"); content.TryPublishValues(langFr); Assert.AreEqual("b", content.GetValue("prop")); @@ -322,7 +322,7 @@ namespace Umbraco.Tests.Models content.SetValue("prop", "a-es", langEs); // cannot publish without a name - Assert.Throws(() => content.TryPublishValues(langFr)); + Assert.IsFalse(content.TryPublishValues(langFr)); // works with a name // and then FR is available, and published From 3cdc0e11428ef07a1b5fa6746c897b0574a2b6ce Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Apr 2018 10:26:32 +1000 Subject: [PATCH 43/97] Removes IShortStringHelper.ReplaceMany, this should just be a normal string extension and removes the need to load in IUmbracoSettings for this simple purpose, this fixes some unit tests --- src/Umbraco.Core/StringExtensions.cs | 22 ++++++++-- .../Strings/DefaultShortStringHelper.cs | 41 +------------------ .../Strings/IShortStringHelper.cs | 17 -------- .../Strings/DefaultShortStringHelperTests.cs | 27 +----------- .../Strings/MockShortStringHelper.cs | 10 ----- .../Strings/StringExtensionsTests.cs | 29 +++++++++---- 6 files changed, 42 insertions(+), 104 deletions(-) diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 8040e6397b..0985e0521c 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -1051,7 +1051,14 @@ namespace Umbraco.Core /// The filtered string. public static string ReplaceMany(this string text, IDictionary replacements) { - return Current.ShortStringHelper.ReplaceMany(text, replacements); + if (text == null) throw new ArgumentNullException(nameof(text)); + if (replacements == null) throw new ArgumentNullException(nameof(replacements)); + + + foreach (KeyValuePair item in replacements) + text = text.Replace(item.Key, item.Value); + + return text; } /// @@ -1062,9 +1069,16 @@ namespace Umbraco.Core /// The replacement character. /// The filtered string. public static string ReplaceMany(this string text, char[] chars, char replacement) - { - return Current.ShortStringHelper.ReplaceMany(text, chars, replacement); - } + { + if (text == null) throw new ArgumentNullException(nameof(text)); + if (chars == null) throw new ArgumentNullException(nameof(chars)); + + + for (int i = 0; i < chars.Length; i++) + text = text.Replace(chars[i], replacement); + + return text; + } // FORMAT STRINGS diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 42a2d3b5d4..3a9d9433f4 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -670,45 +670,6 @@ function validateSafeAlias(input, value, immediate, callback) {{ #endregion - #region ReplaceMany - - /// - /// Returns a new string in which all occurences of specified strings are replaced by other specified strings. - /// - /// The string to filter. - /// The replacements definition. - /// The filtered string. - public virtual string ReplaceMany(string text, IDictionary replacements) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - if (replacements == null) throw new ArgumentNullException(nameof(replacements)); - - - foreach (KeyValuePair item in replacements) - text = text.Replace(item.Key, item.Value); - - return text; - } - - /// - /// Returns a new string in which all occurences of specified characters are replaced by a specified character. - /// - /// The string to filter. - /// The characters to replace. - /// The replacement character. - /// The filtered string. - public virtual string ReplaceMany(string text, char[] chars, char replacement) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - if (chars == null) throw new ArgumentNullException(nameof(chars)); - - - for (int i = 0; i < chars.Length; i++) - text = text.Replace(chars[i], replacement); - - return text; - } - - #endregion + } } diff --git a/src/Umbraco.Core/Strings/IShortStringHelper.cs b/src/Umbraco.Core/Strings/IShortStringHelper.cs index f48d43aa4f..7232b0efe7 100644 --- a/src/Umbraco.Core/Strings/IShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/IShortStringHelper.cs @@ -77,23 +77,6 @@ namespace Umbraco.Core.Strings /// Supports Utf8 and Ascii strings, not Unicode strings. string SplitPascalCasing(string text, char separator); - /// - /// Returns a new string in which all occurences of specified strings are replaced by other specified strings. - /// - /// The string to filter. - /// The replacements definition. - /// The filtered string. - string ReplaceMany(string text, IDictionary replacements); - - /// - /// Returns a new string in which all occurences of specified characters are replaced by a specified character. - /// - /// The string to filter. - /// The characters to replace. - /// The replacement character. - /// The filtered string. - string ReplaceMany(string text, char[] chars, char replacement); - /// /// Cleans a string. /// diff --git a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs index 19cac22287..543b4133f9 100644 --- a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs +++ b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs @@ -596,32 +596,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual(expected, output); } - [Test] // can't do cases with an IDictionary - public void ReplaceManyWithCharMap() - { - const string input = "télévisiön tzvâr ßup   pof"; - const string expected = "television tzvar ssup pof"; - IDictionary replacements = new Dictionary - { - { "é", "e" }, - { "ö", "o" }, - { "â", "a" }, - { "ß", "ss" }, - { " ", " " }, - }; - var output = _helper.ReplaceMany(input, replacements); - Assert.AreEqual(expected, output); - } - - #region Cases - [TestCase("val$id!ate|this|str'ing", "$!'", '-', "val-id-ate|this|str-ing")] - [TestCase("val$id!ate|this|str'ing", "$!'", '*', "val*id*ate|this|str*ing")] - #endregion - public void ReplaceManyByOneChar(string input, string toReplace, char replacement, string expected) - { - var output = _helper.ReplaceMany(input, toReplace.ToArray(), replacement); - Assert.AreEqual(expected, output); - } + #region Cases [TestCase("foo.txt", "foo.txt")] diff --git a/src/Umbraco.Tests/Strings/MockShortStringHelper.cs b/src/Umbraco.Tests/Strings/MockShortStringHelper.cs index dfdb307502..964e1d7ad2 100644 --- a/src/Umbraco.Tests/Strings/MockShortStringHelper.cs +++ b/src/Umbraco.Tests/Strings/MockShortStringHelper.cs @@ -59,16 +59,6 @@ namespace Umbraco.Tests.Strings return "SPLIT-PASCAL-CASING::" + text; } - public string ReplaceMany(string text, IDictionary replacements) - { - return "REPLACE-MANY-A::" + text; - } - - public string ReplaceMany(string text, char[] chars, char replacement) - { - return "REPLACE-MANY-B::" + text; - } - public string CleanString(string text, CleanStringType stringType) { return "CLEAN-STRING-A::" + text; diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index be64ecbbd6..b297f27656 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Linq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; @@ -232,18 +234,31 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("SPLIT-PASCAL-CASING::JUST-ANYTHING", output); } - [Test] + [Test] // can't do cases with an IDictionary public void ReplaceManyWithCharMap() { - var output = "JUST-ANYTHING".ReplaceMany(null); - Assert.AreEqual("REPLACE-MANY-A::JUST-ANYTHING", output); + const string input = "télévisiön tzvâr ßup   pof"; + const string expected = "television tzvar ssup pof"; + IDictionary replacements = new Dictionary + { + { "é", "e" }, + { "ö", "o" }, + { "â", "a" }, + { "ß", "ss" }, + { " ", " " }, + }; + var output = input.ReplaceMany(replacements); + Assert.AreEqual(expected, output); } - [Test] - public void ReplaceManyByOneChar() + #region Cases + [TestCase("val$id!ate|this|str'ing", "$!'", '-', "val-id-ate|this|str-ing")] + [TestCase("val$id!ate|this|str'ing", "$!'", '*', "val*id*ate|this|str*ing")] + #endregion + public void ReplaceManyByOneChar(string input, string toReplace, char replacement, string expected) { - var output = "JUST-ANYTHING".ReplaceMany(new char[] {}, '*'); - Assert.AreEqual("REPLACE-MANY-B::JUST-ANYTHING", output); + var output = input.ReplaceMany(toReplace.ToArray(), replacement); + Assert.AreEqual(expected, output); } } } From 59b6665ff3d669094643d7c604e1b186db8b0e06 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Apr 2018 10:57:51 +1000 Subject: [PATCH 44/97] Removes NiceUrl methods, renames NiceUrl test cases --- ... => ContentFinderByUrlAndTemplateTests.cs} | 96 +- ...UrlTests.cs => ContentFinderByUrlTests.cs} | 314 +++---- ... => ContentFinderByUrlWithDomainsTests.cs} | 374 ++++---- ...rlProviderTests.cs => UrlProviderTests.cs} | 404 ++++----- ...iceUrlRoutesTests.cs => UrlRoutesTests.cs} | 2 +- ...sts.cs => UrlsProviderWithDomainsTests.cs} | 832 +++++++++--------- src/Umbraco.Tests/Umbraco.Tests.csproj | 12 +- src/Umbraco.Web/Editors/ContentController.cs | 2 +- .../RelatedLinksLegacyValueConverter.cs | 2 +- src/Umbraco.Web/RelatedLinksTypeConverter.cs | 2 +- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 2 +- src/Umbraco.Web/UmbracoHelper.cs | 28 +- .../umbraco.presentation/library.cs | 4 +- 13 files changed, 1028 insertions(+), 1046 deletions(-) rename src/Umbraco.Tests/Routing/{ContentFinderByNiceUrlAndTemplateTests.cs => ContentFinderByUrlAndTemplateTests.cs} (94%) rename src/Umbraco.Tests/Routing/{ContentFinderByNiceUrlTests.cs => ContentFinderByUrlTests.cs} (97%) rename src/Umbraco.Tests/Routing/{ContentFinderByNiceUrlWithDomainsTests.cs => ContentFinderByUrlWithDomainsTests.cs} (98%) rename src/Umbraco.Tests/Routing/{NiceUrlProviderTests.cs => UrlProviderTests.cs} (97%) rename src/Umbraco.Tests/Routing/{NiceUrlRoutesTests.cs => UrlRoutesTests.cs} (99%) rename src/Umbraco.Tests/Routing/{NiceUrlsProviderWithDomainsTests.cs => UrlsProviderWithDomainsTests.cs} (98%) diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs similarity index 94% rename from src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs rename to src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs index 7a4be24fbf..5080ab339d 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs @@ -1,52 +1,52 @@ -using Moq; +using Moq; using NUnit.Framework; -using LightInject; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Routing; -using Umbraco.Core.Models; -using Umbraco.Tests.Testing; -using Current = Umbraco.Web.Composing.Current; +using LightInject; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Routing; +using Umbraco.Core.Models; +using Umbraco.Tests.Testing; +using Current = Umbraco.Web.Composing.Current; using Umbraco.Core.Configuration.UmbracoSettings; -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class ContentFinderByNiceUrlAndTemplateTests : BaseWebTest - { - Template CreateTemplate(string alias) - { - var template = new Template(alias, alias); - template.Content = ""; // else saving throws with a dirty internal error - Current.Services.FileService.SaveTemplate(template); - return template; - } - - [TestCase("/blah")] - [TestCase("/default.aspx/blah")] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! - [TestCase("/home/Sub1/blah")] - [TestCase("/Home/Sub1/Blah")] //different cases - [TestCase("/home/Sub1.aspx/blah")] - public void Match_Document_By_Url_With_Template(string urlAsString) - { - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); +namespace Umbraco.Tests.Routing +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] + public class ContentFinderByUrlAndTemplateTests : BaseWebTest + { + Template CreateTemplate(string alias) + { + var template = new Template(alias, alias); + template.Content = ""; // else saving throws with a dirty internal error + Current.Services.FileService.SaveTemplate(template); + return template; + } + + [TestCase("/blah")] + [TestCase("/default.aspx/blah")] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! + [TestCase("/home/Sub1/blah")] + [TestCase("/Home/Sub1/Blah")] //different cases + [TestCase("/home/Sub1.aspx/blah")] + public void Match_Document_By_Url_With_Template(string urlAsString) + { + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettings.Object); - - var template1 = CreateTemplate("test"); - var template2 = CreateTemplate("blah"); - var umbracoContext = GetUmbracoContext(urlAsString, template1.Id, globalSettings:globalSettings.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByUrlAndTemplate(Logger, ServiceContext.FileService); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.IsNotNull(frequest.PublishedContent); - Assert.IsNotNull(frequest.TemplateAlias); - Assert.AreEqual("blah".ToUpperInvariant(), frequest.TemplateAlias.ToUpperInvariant()); - } - } -} + + var template1 = CreateTemplate("test"); + var template2 = CreateTemplate("blah"); + var umbracoContext = GetUmbracoContext(urlAsString, template1.Id, globalSettings:globalSettings.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + var lookup = new ContentFinderByUrlAndTemplate(Logger, ServiceContext.FileService); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.IsNotNull(frequest.PublishedContent); + Assert.IsNotNull(frequest.TemplateAlias); + Assert.AreEqual("blah".ToUpperInvariant(), frequest.TemplateAlias.ToUpperInvariant()); + } + } +} diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs similarity index 97% rename from src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs rename to src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs index 3e8101a212..108abfe446 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs @@ -1,166 +1,166 @@ -using System; -using System.Globalization; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Configuration; -using Umbraco.Core.Models; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class ContentFinderByNiceUrlTests : BaseWebTest - { - [TestCase("/", 1046)] - [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! - [TestCase("/Sub1", 1173)] - [TestCase("/sub1", 1173)] - [TestCase("/sub1.aspx", 1173)] - [TestCase("/home/sub1", -1)] // should fail - - // these two are special. getNiceUrl(1046) returns "/" but getNiceUrl(1172) cannot also return "/" so - // we've made it return "/test-page" => we have to support that url back in the lookup... - [TestCase("/home", 1046)] - [TestCase("/test-page", 1172)] - public void Match_Document_By_Url_Hide_Top_Level(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); +using System; +using System.Globalization; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Routing +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + public class ContentFinderByUrlTests : BaseWebTest + { + [TestCase("/", 1046)] + [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! + [TestCase("/Sub1", 1173)] + [TestCase("/sub1", 1173)] + [TestCase("/sub1.aspx", 1173)] + [TestCase("/home/sub1", -1)] // should fail + + // these two are special. getNiceUrl(1046) returns "/" but getNiceUrl(1172) cannot also return "/" so + // we've made it return "/test-page" => we have to support that url back in the lookup... + [TestCase("/home", 1046)] + [TestCase("/test-page", 1172)] + public void Match_Document_By_Url_Hide_Top_Level(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByUrl(Logger); - - Assert.IsTrue(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); - - // fixme debugging - going further down, the routes cache is NOT empty?! - if (urlString == "/home/sub1") - System.Diagnostics.Debugger.Break(); - - var result = lookup.TryFindContent(frequest); - - if (expectedId > 0) - { - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - else - { - Assert.IsFalse(result); - } - } - - [TestCase("/", 1046)] - [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! - [TestCase("/home", 1046)] - [TestCase("/home/Sub1", 1173)] - [TestCase("/Home/Sub1", 1173)] //different cases - [TestCase("/home/Sub1.aspx", 1173)] - public void Match_Document_By_Url(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + var lookup = new ContentFinderByUrl(Logger); + + Assert.IsTrue(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); + + // fixme debugging - going further down, the routes cache is NOT empty?! + if (urlString == "/home/sub1") + System.Diagnostics.Debugger.Break(); + + var result = lookup.TryFindContent(frequest); + + if (expectedId > 0) + { + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + else + { + Assert.IsFalse(result); + } + } + + [TestCase("/", 1046)] + [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! + [TestCase("/home", 1046)] + [TestCase("/home/Sub1", 1173)] + [TestCase("/Home/Sub1", 1173)] //different cases + [TestCase("/home/Sub1.aspx", 1173)] + public void Match_Document_By_Url(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByUrl(Logger); - - Assert.IsFalse(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - /// - /// This test handles requests with special characters in the URL. - /// - /// - /// - [TestCase("/", 1046)] - [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] - [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] - public void Match_Document_By_Url_With_Special_Characters(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + var lookup = new ContentFinderByUrl(Logger); + + Assert.IsFalse(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + /// + /// This test handles requests with special characters in the URL. + /// + /// + /// + [TestCase("/", 1046)] + [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] + [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] + public void Match_Document_By_Url_With_Special_Characters(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByUrl(Logger); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - - /// - /// This test handles requests with a hostname associated. - /// The logic for handling this goes through the DomainHelper and is a bit different - /// from what happens in a normal request - so it has a separate test with a mocked - /// hostname added. - /// - /// - /// - [TestCase("/", 1046)] - [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] - [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] - public void Match_Document_By_Url_With_Special_Characters_Using_Hostname(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + var lookup = new ContentFinderByUrl(Logger); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + + /// + /// This test handles requests with a hostname associated. + /// The logic for handling this goes through the DomainHelper and is a bit different + /// from what happens in a normal request - so it has a separate test with a mocked + /// hostname added. + /// + /// + /// + [TestCase("/", 1046)] + [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] + [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] + public void Match_Document_By_Url_With_Special_Characters_Using_Hostname(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.Domain = new DomainAndUri(new Domain(1, "mysite", -1, CultureInfo.CurrentCulture, false, true), new Uri("http://mysite/")); - var lookup = new ContentFinderByUrl(Logger); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - - /// - /// This test handles requests with a hostname with special characters associated. - /// The logic for handling this goes through the DomainHelper and is a bit different - /// from what happens in a normal request - so it has a separate test with a mocked - /// hostname added. - /// - /// - /// - [TestCase("/æøå/", 1046)] - [TestCase("/æøå/home/sub1", 1173)] - [TestCase("/æøå/home/sub1/custom-sub-3-with-accént-character", 1179)] - [TestCase("/æøå/home/sub1/custom-sub-4-with-æøå", 1180)] - public void Match_Document_By_Url_With_Special_Characters_In_Hostname(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + frequest.Domain = new DomainAndUri(new Domain(1, "mysite", -1, CultureInfo.CurrentCulture, false, true), new Uri("http://mysite/")); + var lookup = new ContentFinderByUrl(Logger); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + + /// + /// This test handles requests with a hostname with special characters associated. + /// The logic for handling this goes through the DomainHelper and is a bit different + /// from what happens in a normal request - so it has a separate test with a mocked + /// hostname added. + /// + /// + /// + [TestCase("/æøå/", 1046)] + [TestCase("/æøå/home/sub1", 1173)] + [TestCase("/æøå/home/sub1/custom-sub-3-with-accént-character", 1179)] + [TestCase("/æøå/home/sub1/custom-sub-4-with-æøå", 1180)] + public void Match_Document_By_Url_With_Special_Characters_In_Hostname(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.Domain = new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false, true), new Uri("http://mysite/æøå")); - var lookup = new ContentFinderByUrl(Logger); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - } -} + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + frequest.Domain = new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false, true), new Uri("http://mysite/æøå")); + var lookup = new ContentFinderByUrl(Logger); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + } +} diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs similarity index 98% rename from src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs rename to src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs index 2e9bccf398..949467a6fe 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs @@ -1,188 +1,188 @@ -using Moq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Routing -{ - - [TestFixture] - public class ContentFinderByNiceUrlWithDomainsTests : UrlRoutingTestBase - { - void SetDomains3() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"} - }); - - } - - void SetDomains4() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, - new UmbracoDomain("domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} - }); - - } - - protected override string GetXmlContent(int templateId) - { - return @" - - -]> - - - - - This is some content
    ]]> - - - - - - - - - - - - - - - This is some content
    ]]> - - - - - - - - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - - -"; - } - - [TestCase("http://domain1.com/", 1001)] - [TestCase("http://domain1.com/1001-1", 10011)] - [TestCase("http://domain1.com/1001-2/1001-2-1", 100121)] - - public void Lookup_SingleDomain(string url, int expectedId) - { - SetDomains3(); - - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Routing +{ + + [TestFixture] + public class ContentFinderByUrlWithDomainsTests : UrlRoutingTestBase + { + void SetDomains3() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"} + }); + + } + + void SetDomains4() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, + new UmbracoDomain("domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} + }); + + } + + protected override string GetXmlContent(int templateId) + { + return @" + + +]> + + + + + This is some content]]> + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + + +"; + } + + [TestCase("http://domain1.com/", 1001)] + [TestCase("http://domain1.com/1001-1", 10011)] + [TestCase("http://domain1.com/1001-2/1001-2-1", 100121)] + + public void Lookup_SingleDomain(string url, int expectedId) + { + SetDomains3(); + + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - - var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(Container); - var frequest = publishedRouter.CreateRequest(umbracoContext); - - // must lookup domain else lookup by url fails - publishedRouter.FindDomain(frequest); - - var lookup = new ContentFinderByUrl(Logger); - var result = lookup.TryFindContent(frequest); - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - - [TestCase("http://domain1.com/", 1001, "en-US")] - [TestCase("http://domain1.com/en", 10011, "en-US")] - [TestCase("http://domain1.com/en/1001-1-1", 100111, "en-US")] - [TestCase("http://domain1.com/fr", 10012, "fr-FR")] - [TestCase("http://domain1.com/fr/1001-2-1", 100121, "fr-FR")] - [TestCase("http://domain1.com/1001-3", 10013, "en-US")] - - [TestCase("http://domain2.com/1002", 1002, null)] - - [TestCase("http://domain3.com/", 1003, "en-US")] - [TestCase("http://domain3.com/en", 10031, "en-US")] - [TestCase("http://domain3.com/en/1003-1-1", 100311, "en-US")] - [TestCase("http://domain3.com/fr", 10032, "fr-FR")] - [TestCase("http://domain3.com/fr/1003-2-1", 100321, "fr-FR")] - [TestCase("http://domain3.com/1003-3", 10033, "en-US")] - - [TestCase("https://domain1.com/", 1001, "en-US")] - [TestCase("https://domain3.com/", 1001, null)] // because domain3 is explicitely set on http - - public void Lookup_NestedDomains(string url, int expectedId, string expectedCulture) - { - SetDomains4(); - - // defaults depend on test environment - expectedCulture = expectedCulture ?? System.Threading.Thread.CurrentThread.CurrentUICulture.Name; - - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - - var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(Container); - var frequest = publishedRouter.CreateRequest(umbracoContext); - - // must lookup domain else lookup by url fails - publishedRouter.FindDomain(frequest); - Assert.AreEqual(expectedCulture, frequest.Culture.Name); - - var lookup = new ContentFinderByUrl(Logger); - var result = lookup.TryFindContent(frequest); - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - } -} + SettingsForTests.ConfigureSettings(globalSettingsMock.Object); + + var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(Container); + var frequest = publishedRouter.CreateRequest(umbracoContext); + + // must lookup domain else lookup by url fails + publishedRouter.FindDomain(frequest); + + var lookup = new ContentFinderByUrl(Logger); + var result = lookup.TryFindContent(frequest); + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + + [TestCase("http://domain1.com/", 1001, "en-US")] + [TestCase("http://domain1.com/en", 10011, "en-US")] + [TestCase("http://domain1.com/en/1001-1-1", 100111, "en-US")] + [TestCase("http://domain1.com/fr", 10012, "fr-FR")] + [TestCase("http://domain1.com/fr/1001-2-1", 100121, "fr-FR")] + [TestCase("http://domain1.com/1001-3", 10013, "en-US")] + + [TestCase("http://domain2.com/1002", 1002, null)] + + [TestCase("http://domain3.com/", 1003, "en-US")] + [TestCase("http://domain3.com/en", 10031, "en-US")] + [TestCase("http://domain3.com/en/1003-1-1", 100311, "en-US")] + [TestCase("http://domain3.com/fr", 10032, "fr-FR")] + [TestCase("http://domain3.com/fr/1003-2-1", 100321, "fr-FR")] + [TestCase("http://domain3.com/1003-3", 10033, "en-US")] + + [TestCase("https://domain1.com/", 1001, "en-US")] + [TestCase("https://domain3.com/", 1001, null)] // because domain3 is explicitely set on http + + public void Lookup_NestedDomains(string url, int expectedId, string expectedCulture) + { + SetDomains4(); + + // defaults depend on test environment + expectedCulture = expectedCulture ?? System.Threading.Thread.CurrentThread.CurrentUICulture.Name; + + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); + SettingsForTests.ConfigureSettings(globalSettingsMock.Object); + + var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(Container); + var frequest = publishedRouter.CreateRequest(umbracoContext); + + // must lookup domain else lookup by url fails + publishedRouter.FindDomain(frequest); + Assert.AreEqual(expectedCulture, frequest.Culture.Name); + + var lookup = new ContentFinderByUrl(Logger); + var result = lookup.TryFindContent(frequest); + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + } +} diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs similarity index 97% rename from src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs rename to src/Umbraco.Tests/Routing/UrlProviderTests.cs index a4b51bad85..23d3c84bc9 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -1,208 +1,208 @@ -using System; -using System.Collections.Generic; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing; -using Umbraco.Web.PublishedCache.XmlPublishedCache; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class NiceUrlProviderTests : BaseWebTest - { - protected override void Compose() - { - base.Compose(); - Container.Register(); - } - - private IUmbracoSettingsSection _umbracoSettings; - - public override void SetUp() - { - base.SetUp(); - - //generate new mock settings and assign so we can configure in individual tests - _umbracoSettings = SettingsForTests.GenerateMockUmbracoSettings(); - SettingsForTests.ConfigureSettings(_umbracoSettings); - } - - /// - /// This checks that when we retrieve a NiceUrl for multiple items that there are no issues with cache overlap - /// and that they are all cached correctly. - /// - [Test] - public void Ensure_Cache_Is_Correct() - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); +using System; +using System.Collections.Generic; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; +using Umbraco.Web.PublishedCache.XmlPublishedCache; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Routing +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] + public class UrlProviderTests : BaseWebTest + { + protected override void Compose() + { + base.Compose(); + Container.Register(); + } + + private IUmbracoSettingsSection _umbracoSettings; + + public override void SetUp() + { + base.SetUp(); + + //generate new mock settings and assign so we can configure in individual tests + _umbracoSettings = SettingsForTests.GenerateMockUmbracoSettings(); + SettingsForTests.ConfigureSettings(_umbracoSettings); + } + + /// + /// This checks that when we retrieve a NiceUrl for multiple items that there are no issues with cache overlap + /// and that they are all cached correctly. + /// + [Test] + public void Ensure_Cache_Is_Correct() + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new [] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - var requestHandlerMock = Mock.Get(_umbracoSettings.RequestHandler); - requestHandlerMock.Setup(x => x.AddTrailingSlash).Returns(false);// (cached routes have none) - - var samples = new Dictionary { - { 1046, "/home" }, - { 1173, "/home/sub1" }, - { 1174, "/home/sub1/sub2" }, - { 1176, "/home/sub1/sub-3" }, - { 1177, "/home/sub1/custom-sub-1" }, - { 1178, "/home/sub1/custom-sub-2" }, - { 1175, "/home/sub-2" }, - { 1172, "/test-page" } - }; - - foreach (var sample in samples) - { - var result = umbracoContext.UrlProvider.GetUrl(sample.Key); - Assert.AreEqual(sample.Value, result); - } - - var randomSample = new KeyValuePair(1177, "/home/sub1/custom-sub-1"); - for (int i = 0; i < 5; i++) - { - var result = umbracoContext.UrlProvider.GetUrl(randomSample.Key); - Assert.AreEqual(randomSample.Value, result); - } - - var cache = umbracoContext.ContentCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(8, cachedRoutes.Count); - - foreach (var sample in samples) - { - Assert.IsTrue(cachedRoutes.ContainsKey(sample.Key)); - Assert.AreEqual(sample.Value, cachedRoutes[sample.Key]); - } - - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(0, cachedIds.Count); - } - - // test hideTopLevelNodeFromPath false - [TestCase(1046, "/home/")] - [TestCase(1173, "/home/sub1/")] - [TestCase(1174, "/home/sub1/sub2/")] - [TestCase(1176, "/home/sub1/sub-3/")] - [TestCase(1177, "/home/sub1/custom-sub-1/")] - [TestCase(1178, "/home/sub1/custom-sub-2/")] - [TestCase(1175, "/home/sub-2/")] - [TestCase(1172, "/test-page/")] - public void Get_Nice_Url_Not_Hiding_Top_Level(int nodeId, string niceUrlMatch) - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + + var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new [] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + var requestHandlerMock = Mock.Get(_umbracoSettings.RequestHandler); + requestHandlerMock.Setup(x => x.AddTrailingSlash).Returns(false);// (cached routes have none) + + var samples = new Dictionary { + { 1046, "/home" }, + { 1173, "/home/sub1" }, + { 1174, "/home/sub1/sub2" }, + { 1176, "/home/sub1/sub-3" }, + { 1177, "/home/sub1/custom-sub-1" }, + { 1178, "/home/sub1/custom-sub-2" }, + { 1175, "/home/sub-2" }, + { 1172, "/test-page" } + }; + + foreach (var sample in samples) + { + var result = umbracoContext.UrlProvider.GetUrl(sample.Key); + Assert.AreEqual(sample.Value, result); + } + + var randomSample = new KeyValuePair(1177, "/home/sub1/custom-sub-1"); + for (int i = 0; i < 5; i++) + { + var result = umbracoContext.UrlProvider.GetUrl(randomSample.Key); + Assert.AreEqual(randomSample.Value, result); + } + + var cache = umbracoContext.ContentCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual(8, cachedRoutes.Count); + + foreach (var sample in samples) + { + Assert.IsTrue(cachedRoutes.ContainsKey(sample.Key)); + Assert.AreEqual(sample.Value, cachedRoutes[sample.Key]); + } + + var cachedIds = cache.RoutesCache.GetCachedIds(); + Assert.AreEqual(0, cachedIds.Count); + } + + // test hideTopLevelNodeFromPath false + [TestCase(1046, "/home/")] + [TestCase(1173, "/home/sub1/")] + [TestCase(1174, "/home/sub1/sub2/")] + [TestCase(1176, "/home/sub1/sub-3/")] + [TestCase(1177, "/home/sub1/custom-sub-1/")] + [TestCase(1178, "/home/sub1/custom-sub-2/")] + [TestCase(1175, "/home/sub-2/")] + [TestCase(1172, "/test-page/")] + public void Get_Nice_Url_Not_Hiding_Top_Level(int nodeId, string niceUrlMatch) + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - var result = umbracoContext.UrlProvider.GetUrl(nodeId); - Assert.AreEqual(niceUrlMatch, result); - } - - // no need for umbracoUseDirectoryUrls test = should be handled by UriUtilityTests - - // test hideTopLevelNodeFromPath true - [TestCase(1046, "/")] - [TestCase(1173, "/sub1/")] - [TestCase(1174, "/sub1/sub2/")] - [TestCase(1176, "/sub1/sub-3/")] - [TestCase(1177, "/sub1/custom-sub-1/")] - [TestCase(1178, "/sub1/custom-sub-2/")] - [TestCase(1175, "/sub-2/")] - [TestCase(1172, "/test-page/")] // not hidden because not first root - public void Get_Nice_Url_Hiding_Top_Level(int nodeId, string niceUrlMatch) - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); + + var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var result = umbracoContext.UrlProvider.GetUrl(nodeId); + Assert.AreEqual(niceUrlMatch, result); + } + + // no need for umbracoUseDirectoryUrls test = should be handled by UriUtilityTests + + // test hideTopLevelNodeFromPath true + [TestCase(1046, "/")] + [TestCase(1173, "/sub1/")] + [TestCase(1174, "/sub1/sub2/")] + [TestCase(1176, "/sub1/sub-3/")] + [TestCase(1177, "/sub1/custom-sub-1/")] + [TestCase(1178, "/sub1/custom-sub-2/")] + [TestCase(1175, "/sub-2/")] + [TestCase(1172, "/test-page/")] // not hidden because not first root + public void Get_Nice_Url_Hiding_Top_Level(int nodeId, string niceUrlMatch) + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - var result = umbracoContext.UrlProvider.GetUrl(nodeId); - Assert.AreEqual(niceUrlMatch, result); - } - - [Test] - public void Get_Nice_Url_Relative_Or_Absolute() - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + + var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var result = umbracoContext.UrlProvider.GetUrl(nodeId); + Assert.AreEqual(niceUrlMatch, result); + } + + [Test] + public void Get_Nice_Url_Relative_Or_Absolute() + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettings.Object); - - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, umbracoSettings: _umbracoSettings, urlProviders: new[] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - Assert.AreEqual("/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); - Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; - Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); - } - - [Test] - public void Get_Nice_Url_Unpublished() - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, umbracoSettings: _umbracoSettings, urlProviders: new[] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + Assert.AreEqual("/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); + Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; + Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); + } + + [Test] + public void Get_Nice_Url_Unpublished() + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - //mock the Umbraco settings that we need - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); - - Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; - - Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); - } - } -} + + var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, urlProviders: new[] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + //mock the Umbraco settings that we need + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); + + Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; + + Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); + } + } +} diff --git a/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs similarity index 99% rename from src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs rename to src/Umbraco.Tests/Routing/UrlRoutesTests.cs index 2c44a26117..9b291249c9 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs +++ b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs @@ -13,7 +13,7 @@ namespace Umbraco.Tests.Routing // the quirks due to hideTopLevelFromPath and backward compatibility. [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class NiceUrlRoutesTests : TestWithDatabaseBase + public class UrlRoutesTests : TestWithDatabaseBase { #region Test Setup diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs similarity index 98% rename from src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs rename to src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index 938f0cbec6..4f4e35f7e2 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -1,423 +1,423 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.PublishedCache.XmlPublishedCache; -using Umbraco.Web.Routing; -using Umbraco.Core.Composing; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - public class NiceUrlsProviderWithDomainsTests : UrlRoutingTestBase - { - protected override void Compose() - { - base.Compose(); - - Container.RegisterSingleton(_ => Mock.Of()); - Container.Register(); - } - - void SetDomains1() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} - }); - } - - void SetDomains2() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/foo") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} - }); - } - - void SetDomains3() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangFrId, RootContentId = 10011, LanguageIsoCode = "fr-FR"} - }); - } - - void SetDomains4() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} - }); - } - - void SetDomains5() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1a.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1b.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain1a.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain1b.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} - }); - } - - protected override string GetXmlContent(int templateId) - { - return @" - - -]> - - - - - This is some content]]> - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - - -"; - } - - // with one simple domain "domain1.com" - // basic tests - [TestCase(1001, "http://domain1.com", false, "/")] - [TestCase(10011, "http://domain1.com", false, "/1001-1/")] - [TestCase(1002, "http://domain1.com", false, "/1002/")] - // absolute tests - [TestCase(1001, "http://domain1.com", true, "http://domain1.com/")] - [TestCase(10011, "http://domain1.com", true, "http://domain1.com/1001-1/")] - // different current tests - [TestCase(1001, "http://domain2.com", false, "http://domain1.com/")] - [TestCase(10011, "http://domain2.com", false, "http://domain1.com/1001-1/")] - [TestCase(1001, "https://domain1.com", false, "/")] - [TestCase(10011, "https://domain1.com", false, "/1001-1/")] - public void Get_Nice_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, string expected) - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains +using System; +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.PublishedCache.XmlPublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Core.Composing; + +namespace Umbraco.Tests.Routing +{ + [TestFixture] + public class UrlsProviderWithDomainsTests : UrlRoutingTestBase + { + protected override void Compose() + { + base.Compose(); + + Container.RegisterSingleton(_ => Mock.Of()); + Container.Register(); + } + + void SetDomains1() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("domain1.com") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} + }); + } + + void SetDomains2() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("http://domain1.com/foo") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} + }); + } + + void SetDomains3() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangFrId, RootContentId = 10011, LanguageIsoCode = "fr-FR"} + }); + } + + void SetDomains4() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} + }); + } + + void SetDomains5() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1a.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1b.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain1a.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain1b.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} + }); + } + + protected override string GetXmlContent(int templateId) + { + return @" + + +]> + + + + + This is some content]]> + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + + +"; + } + + // with one simple domain "domain1.com" + // basic tests + [TestCase(1001, "http://domain1.com", false, "/")] + [TestCase(10011, "http://domain1.com", false, "/1001-1/")] + [TestCase(1002, "http://domain1.com", false, "/1002/")] + // absolute tests + [TestCase(1001, "http://domain1.com", true, "http://domain1.com/")] + [TestCase(10011, "http://domain1.com", true, "http://domain1.com/1001-1/")] + // different current tests + [TestCase(1001, "http://domain2.com", false, "http://domain1.com/")] + [TestCase(10011, "http://domain2.com", false, "http://domain1.com/1001-1/")] + [TestCase(1001, "https://domain1.com", false, "/")] + [TestCase(10011, "https://domain1.com", false, "/1001-1/")] + public void Get_Nice_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, string expected) + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - SetDomains1(); - - var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); - Assert.AreEqual(expected, result); - } - - // with one complete domain "http://domain1.com/foo" - // basic tests - [TestCase(1001, "http://domain1.com", false, "/foo/")] - [TestCase(10011, "http://domain1.com", false, "/foo/1001-1/")] - [TestCase(1002, "http://domain1.com", false, "/1002/")] - // absolute tests - [TestCase(1001, "http://domain1.com", true, "http://domain1.com/foo/")] - [TestCase(10011, "http://domain1.com", true, "http://domain1.com/foo/1001-1/")] - // different current tests - [TestCase(1001, "http://domain2.com", false, "http://domain1.com/foo/")] - [TestCase(10011, "http://domain2.com", false, "http://domain1.com/foo/1001-1/")] - [TestCase(1001, "https://domain1.com", false, "http://domain1.com/foo/")] - [TestCase(10011, "https://domain1.com", false, "http://domain1.com/foo/1001-1/")] - public void Get_Nice_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool absolute, string expected) - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains1(); + + var currentUri = new Uri(currentUrl); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + Assert.AreEqual(expected, result); + } + + // with one complete domain "http://domain1.com/foo" + // basic tests + [TestCase(1001, "http://domain1.com", false, "/foo/")] + [TestCase(10011, "http://domain1.com", false, "/foo/1001-1/")] + [TestCase(1002, "http://domain1.com", false, "/1002/")] + // absolute tests + [TestCase(1001, "http://domain1.com", true, "http://domain1.com/foo/")] + [TestCase(10011, "http://domain1.com", true, "http://domain1.com/foo/1001-1/")] + // different current tests + [TestCase(1001, "http://domain2.com", false, "http://domain1.com/foo/")] + [TestCase(10011, "http://domain2.com", false, "http://domain1.com/foo/1001-1/")] + [TestCase(1001, "https://domain1.com", false, "http://domain1.com/foo/")] + [TestCase(10011, "https://domain1.com", false, "http://domain1.com/foo/1001-1/")] + public void Get_Nice_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool absolute, string expected) + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - SetDomains2(); - - var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); - Assert.AreEqual(expected, result); - } - - // with one domain, not at root - [TestCase(1001, "http://domain1.com", false, "/1001/")] - [TestCase(10011, "http://domain1.com", false, "/")] - [TestCase(100111, "http://domain1.com", false, "/1001-1-1/")] - [TestCase(1002, "http://domain1.com", false, "/1002/")] - public void Get_Nice_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, string expected) - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains2(); + + var currentUri = new Uri(currentUrl); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + Assert.AreEqual(expected, result); + } + + // with one domain, not at root + [TestCase(1001, "http://domain1.com", false, "/1001/")] + [TestCase(10011, "http://domain1.com", false, "/")] + [TestCase(100111, "http://domain1.com", false, "/1001-1-1/")] + [TestCase(1002, "http://domain1.com", false, "/1002/")] + public void Get_Nice_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, string expected) + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - SetDomains3(); - - var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); - Assert.AreEqual(expected, result); - } - - // with nested domains - [TestCase(1001, "http://domain1.com", false, "/")] - [TestCase(10011, "http://domain1.com", false, "/en/")] - [TestCase(100111, "http://domain1.com", false, "/en/1001-1-1/")] - [TestCase(10012, "http://domain1.com", false, "/fr/")] - [TestCase(100121, "http://domain1.com", false, "/fr/1001-2-1/")] - [TestCase(10013, "http://domain1.com", false, "/1001-3/")] - [TestCase(1002, "http://domain1.com", false, "/1002/")] - [TestCase(1003, "http://domain3.com", false, "/")] - [TestCase(10031, "http://domain3.com", false, "/en/")] - [TestCase(100321, "http://domain3.com", false, "/fr/1003-2-1/")] - public void Get_Nice_Url_NestedDomains(int nodeId, string currentUrl, bool absolute, string expected) - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains3(); + + var currentUri = new Uri(currentUrl); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + Assert.AreEqual(expected, result); + } + + // with nested domains + [TestCase(1001, "http://domain1.com", false, "/")] + [TestCase(10011, "http://domain1.com", false, "/en/")] + [TestCase(100111, "http://domain1.com", false, "/en/1001-1-1/")] + [TestCase(10012, "http://domain1.com", false, "/fr/")] + [TestCase(100121, "http://domain1.com", false, "/fr/1001-2-1/")] + [TestCase(10013, "http://domain1.com", false, "/1001-3/")] + [TestCase(1002, "http://domain1.com", false, "/1002/")] + [TestCase(1003, "http://domain3.com", false, "/")] + [TestCase(10031, "http://domain3.com", false, "/en/")] + [TestCase(100321, "http://domain3.com", false, "/fr/1003-2-1/")] + public void Get_Nice_Url_NestedDomains(int nodeId, string currentUrl, bool absolute, string expected) + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - SetDomains4(); - - var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); - Assert.AreEqual(expected, result); - } - - [Test] - public void Get_Nice_Url_DomainsAndCache() - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains4(); + + var currentUri = new Uri(currentUrl); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + Assert.AreEqual(expected, result); + } + + [Test] + public void Get_Nice_Url_DomainsAndCache() + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - SetDomains4(); - - string ignore; - ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain2.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain2.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain2.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain2.com"), false); - - var cache = umbracoContext.ContentCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(7, cachedRoutes.Count); - - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(0, cachedIds.Count); - - CheckRoute(cachedRoutes, cachedIds, 1001, "1001/"); - CheckRoute(cachedRoutes, cachedIds, 10011, "10011/"); - CheckRoute(cachedRoutes, cachedIds, 100111, "10011/1001-1-1"); - CheckRoute(cachedRoutes, cachedIds, 10012, "10012/"); - CheckRoute(cachedRoutes, cachedIds, 100121, "10012/1001-2-1"); - CheckRoute(cachedRoutes, cachedIds, 10013, "1001/1001-3"); - CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); - - // use the cache - Assert.AreEqual("/", umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/en/", umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/fr/", umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/1001-3/", umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/1002/", umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false)); - - Assert.AreEqual("http://domain1.com/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain2.com"), false)); - } - - private static void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) - { - Assert.IsTrue(routes.ContainsKey(id)); - Assert.AreEqual(route, routes[id]); - Assert.IsFalse(ids.ContainsKey(route)); - } - - [Test] - public void Get_Nice_Url_Relative_Or_Absolute() - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var requestMock = Mock.Get(settings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains4(); + + string ignore; + ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain2.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain2.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain2.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain2.com"), false); + + var cache = umbracoContext.ContentCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual(7, cachedRoutes.Count); + + var cachedIds = cache.RoutesCache.GetCachedIds(); + Assert.AreEqual(0, cachedIds.Count); + + CheckRoute(cachedRoutes, cachedIds, 1001, "1001/"); + CheckRoute(cachedRoutes, cachedIds, 10011, "10011/"); + CheckRoute(cachedRoutes, cachedIds, 100111, "10011/1001-1-1"); + CheckRoute(cachedRoutes, cachedIds, 10012, "10012/"); + CheckRoute(cachedRoutes, cachedIds, 100121, "10012/1001-2-1"); + CheckRoute(cachedRoutes, cachedIds, 10013, "1001/1001-3"); + CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); + + // use the cache + Assert.AreEqual("/", umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/en/", umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/fr/", umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/1001-3/", umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/1002/", umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false)); + + Assert.AreEqual("http://domain1.com/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain2.com"), false)); + } + + private static void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) + { + Assert.IsTrue(routes.ContainsKey(id)); + Assert.AreEqual(route, routes[id]); + Assert.IsFalse(ids.ContainsKey(route)); + } + + [Test] + public void Get_Nice_Url_Relative_Or_Absolute() + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var requestMock = Mock.Get(settings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - SetDomains4(); - - Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); - - Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; - - Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); - } - - [Test] - public void Get_Nice_Url_Alternate() - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains4(); + + Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); + + Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; + + Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); + } + + [Test] + public void Get_Nice_Url_Alternate() + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - - SetDomains5(); - - var url = umbracoContext.UrlProvider.GetUrl(100111, true); - Assert.AreEqual("http://domain1.com/en/1001-1-1/", url); - - var result = umbracoContext.UrlProvider.GetOtherUrls(100111).ToArray(); - - Assert.AreEqual(2, result.Count()); - Assert.IsTrue(result.Contains("http://domain1a.com/en/1001-1-1/")); - Assert.IsTrue(result.Contains("http://domain1b.com/en/1001-1-1/")); - } - } -} + + var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains5(); + + var url = umbracoContext.UrlProvider.GetUrl(100111, true); + Assert.AreEqual("http://domain1.com/en/1001-1-1/", url); + + var result = umbracoContext.UrlProvider.GetOtherUrls(100111).ToArray(); + + Assert.AreEqual(2, result.Count()); + Assert.IsTrue(result.Contains("http://domain1a.com/en/1001-1-1/")); + Assert.IsTrue(result.Contains("http://domain1b.com/en/1001-1-1/")); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 8ae1e4dede..863b875394 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -146,7 +146,12 @@ - + + + + + + @@ -425,9 +430,7 @@ - - @@ -451,10 +454,7 @@ - - - diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 3612ee8847..ffaffa05f4 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -343,7 +343,7 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage GetNiceUrl(int id) { - var url = Umbraco.NiceUrl(id); + var url = Umbraco.Url(id); var response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StringContent(url, Encoding.UTF8, "application/json"); return response; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs index ca6f54fcd8..1e8197ec7a 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs @@ -73,7 +73,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { var content = helper.PublishedContent(udiAttempt.Result); if (content == null) break; - a["link"] = helper.NiceUrl(content.Id); + a["link"] = helper.Url(content.Id); } break; } diff --git a/src/Umbraco.Web/RelatedLinksTypeConverter.cs b/src/Umbraco.Web/RelatedLinksTypeConverter.cs index e06b5b5c62..9ed9398d2e 100644 --- a/src/Umbraco.Web/RelatedLinksTypeConverter.cs +++ b/src/Umbraco.Web/RelatedLinksTypeConverter.cs @@ -67,7 +67,7 @@ namespace Umbraco.Web if (type == "internal") { var linkId = a.Value("link"); - var link = umbracoHelper.NiceUrl(linkId); + var link = umbracoHelper.Url(linkId); a["link"] = link; } } diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 66a7115dc7..262c2c80e3 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -329,7 +329,7 @@ namespace Umbraco.Web.Search var intId = m.Id.TryConvertTo(); if (intId.Success) { - m.AdditionalData["Url"] = umbracoHelper.NiceUrl(intId.Result); + m.AdditionalData["Url"] = umbracoHelper.Url(intId.Result); } } return mapped; diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 2386e4abef..fca5c200b7 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -331,19 +331,9 @@ namespace Umbraco.Web #endregion - #region NiceUrls - - /// - /// Returns a string with a friendly url from a node. - /// IE.: Instead of having /482 (id) as an url, you can have - /// /screenshots/developer/macros (spoken url) - /// - /// Identifier for the node that should be returned - /// String with a friendly url from a node - public string NiceUrl(int nodeId) - { - return Url(nodeId); - } + #region Urls + + //TODO: We will need an optional culture parameter, by default it will be the current thread culture /// /// Gets the url of a content identified by its identifier. @@ -365,16 +355,8 @@ namespace Umbraco.Web { return UrlProvider.GetUrl(contentId, mode); } - - /// - /// This method will always add the domain to the path if the hostnames are set up correctly. - /// - /// Identifier for the node that should be returned - /// String with a friendly url with full domain from a node - public string NiceUrlWithDomain(int nodeId) - { - return UrlAbsolute(nodeId); - } + + //TODO: We will need an optional culture parameter, by default it will be the current thread culture /// /// Gets the absolute url of a content identified by its identifier. diff --git a/src/Umbraco.Web/umbraco.presentation/library.cs b/src/Umbraco.Web/umbraco.presentation/library.cs index d85fce59cd..5a2353cdde 100644 --- a/src/Umbraco.Web/umbraco.presentation/library.cs +++ b/src/Umbraco.Web/umbraco.presentation/library.cs @@ -127,7 +127,7 @@ namespace umbraco /// String with a friendly url from a node public static string NiceUrl(int nodeID) { - return GetUmbracoHelper().NiceUrl(nodeID); + return GetUmbracoHelper().Url(nodeID); } /// @@ -137,7 +137,7 @@ namespace umbraco /// String with a friendly url with full domain from a node public static string NiceUrlWithDomain(int nodeId) { - return GetUmbracoHelper().NiceUrlWithDomain(nodeId); + return GetUmbracoHelper().UrlAbsolute(nodeId); } /// From ad6a7456811f772f92bc53d6a474306c7942d270 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Apr 2018 11:08:20 +1000 Subject: [PATCH 45/97] Changes CultureInfo to string where required, fixes tests --- .../PublishedContentTestElements.cs | 8 ++++---- .../TestControllerActivatorBase.cs | 2 +- .../Testing/TestingTests/MockTests.cs | 2 +- .../Web/TemplateUtilitiesTests.cs | 4 ++-- .../PublishedCache/IPublishedContentCache.cs | 8 ++++---- .../PublishedCache/NuCache/CacheKeys.cs | 8 ++++---- .../PublishedCache/NuCache/ContentCache.cs | 14 +++++++------- .../XmlPublishedCache/PublishedContentCache.cs | 8 ++++---- src/Umbraco.Web/PublishedContentExtensions.cs | 4 ++-- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 2 +- .../Routing/ContentFinderByLegacy404.cs | 2 +- src/Umbraco.Web/Routing/ContentFinderByUrl.cs | 4 +++- .../Routing/CustomRouteUrlProvider.cs | 2 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 4 ++-- src/Umbraco.Web/Routing/IUrlProvider.cs | 2 +- src/Umbraco.Web/Routing/UrlProvider.cs | 18 +++++++++--------- 16 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs index 9e416015b4..6e5953544f 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs @@ -59,22 +59,22 @@ namespace Umbraco.Tests.PublishedContent _content.Clear(); } - public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, CultureInfo culture = null) + public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null) { throw new NotImplementedException(); } - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, CultureInfo culture = null) + public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) { throw new NotImplementedException(); } - public string GetRouteById(bool preview, int contentId, CultureInfo culture = null) + public string GetRouteById(bool preview, int contentId, string culture = null) { throw new NotImplementedException(); } - public string GetRouteById(int contentId, CultureInfo culture = null) + public string GetRouteById(int contentId, string culture = null) { throw new NotImplementedException(); } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 05e9a39551..96404b1e59 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -153,7 +153,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting true); //replace it var urlHelper = new Mock(); - urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var membershipHelper = new MembershipHelper(umbCtx, Mock.Of(), Mock.Of()); diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 7575bb3d1f..59bc24ed10 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -74,7 +74,7 @@ namespace Umbraco.Tests.Testing.TestingTests var umbracoContext = TestObjects.GetUmbracoContextMock(); var urlProviderMock = new Mock(); - urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var urlProvider = urlProviderMock.Object; diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index ed06948e3f..d35b4e5823 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -73,8 +73,8 @@ namespace Umbraco.Tests.Web //setup a mock url provider which we'll use fo rtesting var testUrlProvider = new Mock(); - testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((UmbracoContext umbCtx, int id, Uri url, UrlProviderMode mode) => + testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((UmbracoContext umbCtx, int id, Uri url, UrlProviderMode mode, string culture) => { return "/my-test-url"; }); diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs index 2d7c93d93e..3254207280 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache /// If is null then the settings value is used. /// The value of overrides defaults. /// - IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, CultureInfo culture = null); + IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null); /// /// Gets content identified by a route. @@ -31,7 +31,7 @@ namespace Umbraco.Web.PublishedCache /// If is null then the settings value is used. /// Considers published or unpublished content depending on defaults. /// - IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, CultureInfo culture = null); + IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null); /// /// Gets the route for a content identified by its unique identifier. @@ -40,7 +40,7 @@ namespace Umbraco.Web.PublishedCache /// The content unique identifier. /// The route. /// The value of overrides defaults. - string GetRouteById(bool preview, int contentId, CultureInfo culture = null); + string GetRouteById(bool preview, int contentId, string culture = null); /// /// Gets the route for a content identified by its unique identifier. @@ -48,6 +48,6 @@ namespace Umbraco.Web.PublishedCache /// The content unique identifier. /// The route. /// Considers published or unpublished content depending on defaults. - string GetRouteById(int contentId, CultureInfo culture = null); + string GetRouteById(int contentId, string culture = null); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs index e1b86b9ad6..5bdee5a416 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs @@ -13,9 +13,9 @@ namespace Umbraco.Web.PublishedCache.NuCache } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string LangId(CultureInfo culture) + private static string LangId(string culture) { - return culture != null ? ("-L:" + culture.Name) : string.Empty; + return culture != null ? ("-L:" + culture) : string.Empty; } public static string PublishedContentChildren(Guid contentUid, bool previewing) @@ -57,12 +57,12 @@ namespace Umbraco.Web.PublishedCache.NuCache // a valid ID in the database at that point, whereas content and properties // may be virtual (and not in umbracoNode). - public static string ContentCacheRouteByContent(int id, bool previewing, CultureInfo culture) + public static string ContentCacheRouteByContent(int id, bool previewing, string culture) { return "NuCache.ContentCache.RouteByContent[" + DraftOrPub(previewing) + id + LangId(culture) + "]"; } - public static string ContentCacheContentByRoute(string route, bool previewing, CultureInfo culture) + public static string ContentCacheContentByRoute(string route, bool previewing, string culture) { return "NuCache.ContentCache.ContentByRoute[" + DraftOrPub(previewing) + route + LangId(culture) + "]"; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index c6d380ca55..fc95557dd1 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -55,12 +55,12 @@ namespace Umbraco.Web.PublishedCache.NuCache // at the moment we try our best to be backward compatible, but really, // should get rid of hideTopLevelNode and other oddities entirely, eventually - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, CultureInfo culture = null) + public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) { return GetByRoute(PreviewDefault, route, hideTopLevelNode, culture); } - public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, CultureInfo culture = null) + public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null) { if (route == null) throw new ArgumentNullException(nameof(route)); @@ -69,7 +69,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return cache.GetCacheItem(key, () => GetByRouteInternal(preview, route, hideTopLevelNode, culture)); } - private IPublishedContent GetByRouteInternal(bool preview, string route, bool? hideTopLevelNode, CultureInfo culture) + private IPublishedContent GetByRouteInternal(bool preview, string route, bool? hideTopLevelNode, string culture) { hideTopLevelNode = hideTopLevelNode ?? HideTopLevelNodeFromPath; // default = settings @@ -120,19 +120,19 @@ namespace Umbraco.Web.PublishedCache.NuCache return content; } - public string GetRouteById(int contentId, CultureInfo culture = null) + public string GetRouteById(int contentId, string culture = null) { return GetRouteById(PreviewDefault, contentId, culture); } - public string GetRouteById(bool preview, int contentId, CultureInfo culture = null) + public string GetRouteById(bool preview, int contentId, string culture = null) { var cache = (preview == false || PublishedSnapshotService.FullCacheWhenPreviewing) ? _elementsCache : _snapshotCache; var key = CacheKeys.ContentCacheRouteByContent(contentId, preview, culture); return cache.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null, culture)); } - private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode, CultureInfo culture) + private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode, string culture) { var node = GetById(preview, contentId); if (node == null) @@ -168,7 +168,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return route; } - private IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList parts, int start, CultureInfo culture) + private IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList parts, int start, string culture) { var i = start; while (content != null && i < parts.Count) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index 01ab37554e..57d9e0a980 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -64,7 +64,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache #region Routes - public virtual IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, CultureInfo culture = null) + public virtual IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null) { if (route == null) throw new ArgumentNullException(nameof(route)); @@ -108,12 +108,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _routesCache.Store(content.Id, route, true); // trusted route } - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, CultureInfo culture = null) + public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) { return GetByRoute(PreviewDefault, route, hideTopLevelNode); } - public virtual string GetRouteById(bool preview, int contentId, CultureInfo culture = null) + public virtual string GetRouteById(bool preview, int contentId, string culture = null) { // try to get from cache if not previewing var route = preview || _routesCache == null ? null : _routesCache.GetRoute(contentId); @@ -138,7 +138,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return route; } - public string GetRouteById(int contentId, CultureInfo culture = null) + public string GetRouteById(int contentId, string culture = null) { return GetRouteById(PreviewDefault, contentId, culture); } diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index e68365416d..d11a168b88 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1253,11 +1253,11 @@ namespace Umbraco.Web /// /// /// - public static string GetUrlName(this IPublishedContent content, ILocalizationService localizationService, CultureInfo culture = null) + public static string GetUrlName(this IPublishedContent content, ILocalizationService localizationService, string culture = null) { if (content.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) { - var cultureCode = culture != null ? culture.Name : localizationService.GetDefaultVariantLanguage()?.IsoCode; + var cultureCode = culture ?? localizationService.GetDefaultVariantLanguage()?.IsoCode; if (cultureCode != null && content.CultureNames.TryGetValue(cultureCode, out var cultureName)) { return cultureName.UrlName; diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 67e2d56fcb..5e3f8ea919 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it should return null. /// - public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, CultureInfo culture = null) + public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null) { return null; // we have nothing to say } diff --git a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs index 090b7d421d..6cc3b74972 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web.Routing while (pos > 1) { route = route.Substring(0, pos); - node = frequest.UmbracoContext.ContentCache.GetByRoute(route, culture: frequest.Culture); + node = frequest.UmbracoContext.ContentCache.GetByRoute(route, culture: frequest?.Culture?.Name); if (node != null) break; pos = route.LastIndexOf('/'); } diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs index 66b8024c6b..042ca5882f 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs @@ -44,9 +44,11 @@ namespace Umbraco.Web.Routing /// The document node, or null. protected IPublishedContent FindContent(PublishedRequest docreq, string route) { + if (docreq == null) throw new System.ArgumentNullException(nameof(docreq)); + Logger.Debug(() => $"Test route \"{route}\""); - var node = docreq.UmbracoContext.ContentCache.GetByRoute(route, culture: docreq.Culture); + var node = docreq.UmbracoContext.ContentCache.GetByRoute(route, culture: docreq.Culture?.Name); if (node != null) { docreq.PublishedContent = node; diff --git a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs index 7a3d8fffed..926db253c8 100644 --- a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs +++ b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.Routing /// /// /// - public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, CultureInfo culture = null) + public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null) { if (umbracoContext == null) return null; if (umbracoContext.PublishedRequest == null) return null; diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index c451d91565..788a7a809b 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -43,7 +43,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it should return null. /// - public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, CultureInfo culture = null) + public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null) { if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", "current"); @@ -116,7 +116,7 @@ namespace Umbraco.Web.Routing foreach (var d in domainUris) { //although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok - var route = umbracoContext.ContentCache.GetRouteById(id, d.Culture); + var route = umbracoContext.ContentCache.GetRouteById(id, d?.Culture?.Name); if (route == null) continue; //need to strip off the leading ID for the route diff --git a/src/Umbraco.Web/Routing/IUrlProvider.cs b/src/Umbraco.Web/Routing/IUrlProvider.cs index 086f9c4767..e4c6e23022 100644 --- a/src/Umbraco.Web/Routing/IUrlProvider.cs +++ b/src/Umbraco.Web/Routing/IUrlProvider.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it should return null. /// - string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, CultureInfo culture = null); + string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null); /// /// Gets the other urls of a published content. diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 3fb0d6fd1d..422fb4d4e7 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -77,7 +77,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on Mode and on the current url. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(Guid id, CultureInfo culture = null) + public string GetUrl(Guid id, string culture = null) { var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); return GetUrl(intId.Success ? intId.Result : -1, culture); @@ -94,7 +94,7 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(Guid id, bool absolute, CultureInfo culture = null) + public string GetUrl(Guid id, bool absolute, string culture = null) { var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); return GetUrl(intId.Success ? intId.Result : -1, absolute, culture); @@ -112,7 +112,7 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(Guid id, Uri current, bool absolute, CultureInfo culture = null) + public string GetUrl(Guid id, Uri current, bool absolute, string culture = null) { var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); return GetUrl(intId.Success ? intId.Result : -1, current, absolute, culture); @@ -128,7 +128,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on the current url. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(Guid id, UrlProviderMode mode, CultureInfo culture = null) + public string GetUrl(Guid id, UrlProviderMode mode, string culture = null) { var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); return GetUrl(intId.Success ? intId.Result : -1, mode, culture); @@ -143,7 +143,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on Mode and on the current url. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, CultureInfo culture = null) + public string GetUrl(int id, string culture = null) { return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, Mode, culture); } @@ -159,7 +159,7 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, bool absolute, CultureInfo culture = null) + public string GetUrl(int id, bool absolute, string culture = null) { var mode = absolute ? UrlProviderMode.Absolute : Mode; return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode, culture); @@ -177,7 +177,7 @@ namespace Umbraco.Web.Routing /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, Uri current, bool absolute, CultureInfo culture = null) + public string GetUrl(int id, Uri current, bool absolute, string culture = null) { var mode = absolute ? UrlProviderMode.Absolute : Mode; return GetUrl(id, current, mode, culture); @@ -193,7 +193,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on the current url. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, UrlProviderMode mode, CultureInfo culture = null) + public string GetUrl(int id, UrlProviderMode mode, string culture = null) { return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode, culture); } @@ -209,7 +209,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, Uri current, UrlProviderMode mode, CultureInfo culture = null) + public string GetUrl(int id, Uri current, UrlProviderMode mode, string culture = null) { var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, id, current, mode, culture)) .FirstOrDefault(u => u != null); From 73567ffdce3ebe2c01db50c0cbf13de5e20021e0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Apr 2018 11:38:50 +1000 Subject: [PATCH 46/97] Fixes IPublishedShapshot and friends --- .../PublishedContentCacheTests.cs | 2 +- .../Published/ConvertersTests.cs | 4 +- .../Published/NestedContentTests.cs | 2 +- .../Published/PropertyCacheLevelTests.cs | 2 +- .../PublishedContentMoreTests.cs | 4 +- ...tElements.cs => SolidPublishedSnapshot.cs} | 690 +++++++++--------- src/Umbraco.Tests/Routing/RoutesCacheTests.cs | 2 +- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 7 +- .../Routing/UrlsProviderWithDomainsTests.cs | 14 +- .../TestControllerActivatorBase.cs | 2 +- .../Stubs/TestPublishedSnapshotAccessor.cs | 2 +- .../TestHelpers/TestObjects-Mocks.cs | 2 +- .../TestHelpers/TestWithDatabaseBase.cs | 19 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../Web/Mvc/SurfaceControllerTests.cs | 2 +- .../Web/Mvc/UmbracoViewPageTests.cs | 2 +- src/Umbraco.Web/Composing/Current.cs | 2 +- ...ishedShapshot.cs => IPublishedSnapshot.cs} | 2 +- .../IPublishedSnapshotAccessor.cs | 4 +- .../IPublishedSnapshotService.cs | 4 +- .../PublishedCache/NuCache/Property.cs | 6 +- .../NuCache/PublishedContent.cs | 8 +- ...lishedShapshot.cs => PublishedSnapshot.cs} | 12 +- .../NuCache/PublishedSnapshotService.cs | 18 +- .../PublishedSnapshotServiceBase.cs | 4 +- ...UmbracoContextPublishedSnapshotAccessor.cs | 4 +- ...lishedShapshot.cs => PublishedSnapshot.cs} | 6 +- .../PublishedSnapshotService.cs | 4 +- .../XmlPublishedCache/XmlStore.cs | 2 +- .../Routing/ContentFinderByLegacy404.cs | 2 +- src/Umbraco.Web/Routing/PublishedRouter.cs | 8 +- src/Umbraco.Web/Security/MembershipHelper.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 6 +- src/Umbraco.Web/UmbracoContext.cs | 14 +- 34 files changed, 435 insertions(+), 431 deletions(-) rename src/Umbraco.Tests/PublishedContent/{PublishedContentTestElements.cs => SolidPublishedSnapshot.cs} (96%) rename src/Umbraco.Web/PublishedCache/{IPublishedShapshot.cs => IPublishedSnapshot.cs} (97%) rename src/Umbraco.Web/PublishedCache/NuCache/{PublishedShapshot.cs => PublishedSnapshot.cs} (88%) rename src/Umbraco.Web/PublishedCache/XmlPublishedCache/{PublishedShapshot.cs => PublishedSnapshot.cs} (92%) diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 304c20c78f..234b31d5a0 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var xmlStore = new XmlStore(() => _xml, null, null, null); var cacheProvider = new StaticCacheProvider(); var domainCache = new DomainCache(ServiceContext.DomainService, ServiceContext.LocalizationService); - var publishedShapshot = new Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedShapshot( + var publishedShapshot = new Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedSnapshot( new PublishedContentCache(xmlStore, domainCache, cacheProvider, globalSettings, new SiteDomainHelper(), ContentTypesCache, null, null), new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, cacheProvider, ContentTypesCache), new PublishedMemberCache(null, cacheProvider, Current.Services.MemberService, ContentTypesCache), diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index bc17a7fb03..0c47a551bc 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -75,7 +75,7 @@ namespace Umbraco.Tests.Published var cacheMock = new Mock(); var cacheContent = new Dictionary(); cacheMock.Setup(x => x.GetById(It.IsAny())).Returns(id => cacheContent.TryGetValue(id, out IPublishedContent content) ? content : null); - var publishedSnapshotMock = new Mock(); + var publishedSnapshotMock = new Mock(); publishedSnapshotMock.Setup(x => x.Content).Returns(cacheMock.Object); var publishedSnapshotAccessorMock = new Mock(); publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot).Returns(publishedSnapshotMock.Object); @@ -164,7 +164,7 @@ namespace Umbraco.Tests.Published var cacheMock = new Mock(); var cacheContent = new Dictionary(); cacheMock.Setup(x => x.GetById(It.IsAny())).Returns(id => cacheContent.TryGetValue(id, out IPublishedContent content) ? content : null); - var publishedSnapshotMock = new Mock(); + var publishedSnapshotMock = new Mock(); publishedSnapshotMock.Setup(x => x.Content).Returns(cacheMock.Object); var publishedSnapshotAccessorMock = new Mock(); publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot).Returns(publishedSnapshotMock.Object); diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 28e103ad7c..c806930704 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -104,7 +104,7 @@ namespace Umbraco.Tests.Published }); var contentCache = new Mock(); - var publishedSnapshot = new Mock(); + var publishedSnapshot = new Mock(); // mocked published snapshot returns a content cache publishedSnapshot diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index 49d1707567..950d81ee36 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -121,7 +121,7 @@ namespace Umbraco.Tests.Published var elementsCache = new DictionaryCacheProvider(); var snapshotCache = new DictionaryCacheProvider(); - var publishedSnapshot = new Mock(); + var publishedSnapshot = new Mock(); publishedSnapshot.Setup(x => x.SnapshotCache).Returns(snapshotCache); publishedSnapshot.Setup(x => x.ElementsCache).Returns(elementsCache); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 3e715792cb..61c37d6a51 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -197,13 +197,13 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(2, result[1].Id); } - private static SolidPublishedShapshot CreatePublishedSnapshot() + 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 SolidPublishedShapshot(); + var caches = new SolidPublishedSnapshot(); var cache = caches.InnerContentCache; var props = new[] diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs similarity index 96% rename from src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs rename to src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 6e5953544f..4b7a131bd0 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -1,347 +1,347 @@ -using System; -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Globalization; -using System.Linq; -using Moq; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web; -using Umbraco.Web.PublishedCache; - -namespace Umbraco.Tests.PublishedContent -{ - class SolidPublishedShapshot : IPublishedShapshot - { - public readonly SolidPublishedContentCache InnerContentCache = new SolidPublishedContentCache(); - public readonly SolidPublishedContentCache InnerMediaCache = new SolidPublishedContentCache(); - - public IPublishedContentCache Content => InnerContentCache; - - public IPublishedMediaCache Media => InnerMediaCache; - - public IPublishedMemberCache Members => null; - - public IDomainCache Domains => null; - - public IDisposable ForcedPreview(bool forcedPreview, Action callback = null) - { - throw new NotImplementedException(); - } - - public void Resync() - { } - - public ICacheProvider SnapshotCache => null; - - public ICacheProvider ElementsCache => null; - } - - class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache, IPublishedMediaCache - { - private readonly Dictionary _content = new Dictionary(); - - public SolidPublishedContentCache() - : base(false) - { } - - public void Add(SolidPublishedContent content) - { - _content[content.Id] = content.CreateModel(); - } - - public void Clear() - { - _content.Clear(); - } - - public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null) - { - throw new NotImplementedException(); - } - - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) - { - throw new NotImplementedException(); - } - - public string GetRouteById(bool preview, int contentId, string culture = null) - { - throw new NotImplementedException(); - } - - public string GetRouteById(int contentId, string culture = null) - { - throw new NotImplementedException(); - } - - public override IPublishedContent GetById(bool preview, int contentId) - { - return _content.ContainsKey(contentId) ? _content[contentId] : null; - } - - public override IPublishedContent GetById(bool preview, Guid contentId) - { - throw new NotImplementedException(); - } - - public override bool HasById(bool preview, int contentId) - { - return _content.ContainsKey(contentId); - } - - public override IEnumerable GetAtRoot(bool preview) - { - return _content.Values.Where(x => x.Parent == null); - } - - public override IPublishedContent GetSingleByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override IPublishedContent GetSingleByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override IEnumerable GetByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override IEnumerable GetByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override System.Xml.XPath.XPathNavigator CreateNavigator(bool preview) - { - throw new NotImplementedException(); - } - - public override System.Xml.XPath.XPathNavigator CreateNodeNavigator(int id, bool preview) - { - throw new NotImplementedException(); - } - - public override bool HasContent(bool preview) - { - return _content.Count > 0; - } - - public override PublishedContentType GetContentType(int id) - { - throw new NotImplementedException(); - } - - public override PublishedContentType GetContentType(string alias) - { - throw new NotImplementedException(); - } - - public override IEnumerable GetByContentType(PublishedContentType contentType) - { - throw new NotImplementedException(); - } - } - - class SolidPublishedContent : IPublishedContent - { - #region Constructor - - public SolidPublishedContent(PublishedContentType contentType) - { - // initialize boring stuff - TemplateId = 0; - WriterName = CreatorName = string.Empty; - WriterId = CreatorId = 0; - CreateDate = UpdateDate = DateTime.Now; - Version = Guid.Empty; - IsDraft = false; - - ContentType = contentType; - DocumentTypeAlias = contentType.Alias; - DocumentTypeId = contentType.Id; - } - - #endregion - - #region Content - - public int Id { get; set; } - public Guid Key { get; set; } - public int TemplateId { get; set; } - public int SortOrder { get; set; } +using System.Linq; +using Moq; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.PublishedContent +{ + class SolidPublishedSnapshot : IPublishedSnapshot + { + public readonly SolidPublishedContentCache InnerContentCache = new SolidPublishedContentCache(); + public readonly SolidPublishedContentCache InnerMediaCache = new SolidPublishedContentCache(); + + public IPublishedContentCache Content => InnerContentCache; + + public IPublishedMediaCache Media => InnerMediaCache; + + public IPublishedMemberCache Members => null; + + public IDomainCache Domains => null; + + public IDisposable ForcedPreview(bool forcedPreview, Action callback = null) + { + throw new NotImplementedException(); + } + + public void Resync() + { } + + public ICacheProvider SnapshotCache => null; + + public ICacheProvider ElementsCache => null; + } + + class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache, IPublishedMediaCache + { + private readonly Dictionary _content = new Dictionary(); + + public SolidPublishedContentCache() + : base(false) + { } + + public void Add(SolidPublishedContent content) + { + _content[content.Id] = content.CreateModel(); + } + + public void Clear() + { + _content.Clear(); + } + + public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null) + { + throw new NotImplementedException(); + } + + public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) + { + throw new NotImplementedException(); + } + + public string GetRouteById(bool preview, int contentId, string culture = null) + { + throw new NotImplementedException(); + } + + public string GetRouteById(int contentId, string culture = null) + { + throw new NotImplementedException(); + } + + public override IPublishedContent GetById(bool preview, int contentId) + { + return _content.ContainsKey(contentId) ? _content[contentId] : null; + } + + public override IPublishedContent GetById(bool preview, Guid contentId) + { + throw new NotImplementedException(); + } + + public override bool HasById(bool preview, int contentId) + { + return _content.ContainsKey(contentId); + } + + public override IEnumerable GetAtRoot(bool preview) + { + return _content.Values.Where(x => x.Parent == null); + } + + public override IPublishedContent GetSingleByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public override IPublishedContent GetSingleByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public override IEnumerable GetByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public override IEnumerable GetByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public override System.Xml.XPath.XPathNavigator CreateNavigator(bool preview) + { + throw new NotImplementedException(); + } + + public override System.Xml.XPath.XPathNavigator CreateNodeNavigator(int id, bool preview) + { + throw new NotImplementedException(); + } + + public override bool HasContent(bool preview) + { + return _content.Count > 0; + } + + public override PublishedContentType GetContentType(int id) + { + throw new NotImplementedException(); + } + + public override PublishedContentType GetContentType(string alias) + { + throw new NotImplementedException(); + } + + public override IEnumerable GetByContentType(PublishedContentType contentType) + { + throw new NotImplementedException(); + } + } + + class SolidPublishedContent : IPublishedContent + { + #region Constructor + + public SolidPublishedContent(PublishedContentType contentType) + { + // initialize boring stuff + TemplateId = 0; + WriterName = CreatorName = string.Empty; + WriterId = CreatorId = 0; + CreateDate = UpdateDate = DateTime.Now; + Version = Guid.Empty; + IsDraft = false; + + ContentType = contentType; + DocumentTypeAlias = contentType.Alias; + DocumentTypeId = contentType.Id; + } + + #endregion + + #region Content + + public int Id { get; set; } + public Guid Key { get; set; } + public int TemplateId { get; set; } + public int SortOrder { get; set; } public string Name { get; set; } - public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); - public string UrlName { get; set; } - public string DocumentTypeAlias { get; private set; } - public int DocumentTypeId { get; private set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - public string Url { get; set; } - - public PublishedItemType ItemType { get { return PublishedItemType.Content; } } - public bool IsDraft { get; set; } - - #endregion - - #region Tree - - public int ParentId { get; set; } - public IEnumerable ChildIds { get; set; } - - public IPublishedContent Parent { get; set; } - public IEnumerable Children { get; set; } - - #endregion - - #region ContentType - - public PublishedContentType ContentType { get; private set; } - - #endregion - - #region Properties - - public IEnumerable Properties { get; set; } - - public IPublishedProperty GetProperty(string alias) - { - return Properties.FirstOrDefault(p => p.Alias.InvariantEquals(alias)); - } - - public IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - while (content != null && (property == null || property.HasValue() == false)) - { - content = content.Parent; - property = content == null ? null : content.GetProperty(alias); - } - - return property; - } - - public object this[string alias] - { - get - { - var property = GetProperty(alias); - return property == null || property.HasValue() == false ? null : property.GetValue(); - } - } - - #endregion - } - - class SolidPublishedProperty : IPublishedProperty - { - public string Alias { get; set; } - public object SolidSourceValue { get; set; } - public object SolidValue { get; set; } - 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; - } - - [PublishedModel("ContentType2")] - internal class ContentType2 : PublishedContentModel - { - #region Plumbing - - public ContentType2(IPublishedContent content) - : base(content) - { } - - #endregion - - public int Prop1 => this.Value("prop1"); - } - - [PublishedModel("ContentType2Sub")] - internal class ContentType2Sub : ContentType2 - { - #region Plumbing - - public ContentType2Sub(IPublishedContent content) - : base(content) - { } - - #endregion - } - - class PublishedContentStrong1 : PublishedContentModel - { - public PublishedContentStrong1(IPublishedContent content) - : base(content) - { } - - public int StrongValue => this.Value("strongValue"); - } - - class PublishedContentStrong1Sub : PublishedContentStrong1 - { - public PublishedContentStrong1Sub(IPublishedContent content) - : base(content) - { } - - public int AnotherValue => this.Value("anotherValue"); - } - - class PublishedContentStrong2 : PublishedContentModel - { - public PublishedContentStrong2(IPublishedContent content) - : base(content) - { } - - public int StrongValue => this.Value("strongValue"); - } - - class AutoPublishedContentType : PublishedContentType - { - private static readonly PublishedPropertyType Default; - - static AutoPublishedContentType() - { - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(Mock.Of())) { Id = 666 }); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - Default = factory.CreatePropertyType("*", 666); - } - - public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) - : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.InvariantNeutral) - { } - - public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) - : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.InvariantNeutral) - { } - - public override PublishedPropertyType GetPropertyType(string alias) - { - var propertyType = base.GetPropertyType(alias); - return propertyType ?? Default; - } - } -} + public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); + public string UrlName { get; set; } + public string DocumentTypeAlias { get; private set; } + public int DocumentTypeId { get; private set; } + public string WriterName { get; set; } + public string CreatorName { get; set; } + public int WriterId { get; set; } + public int CreatorId { get; set; } + public string Path { get; set; } + public DateTime CreateDate { get; set; } + public DateTime UpdateDate { get; set; } + public Guid Version { get; set; } + public int Level { get; set; } + public string Url { get; set; } + + public PublishedItemType ItemType { get { return PublishedItemType.Content; } } + public bool IsDraft { get; set; } + + #endregion + + #region Tree + + public int ParentId { get; set; } + public IEnumerable ChildIds { get; set; } + + public IPublishedContent Parent { get; set; } + public IEnumerable Children { get; set; } + + #endregion + + #region ContentType + + public PublishedContentType ContentType { get; private set; } + + #endregion + + #region Properties + + public IEnumerable Properties { get; set; } + + public IPublishedProperty GetProperty(string alias) + { + return Properties.FirstOrDefault(p => p.Alias.InvariantEquals(alias)); + } + + public IPublishedProperty GetProperty(string alias, bool recurse) + { + var property = GetProperty(alias); + if (recurse == false) return property; + + IPublishedContent content = this; + while (content != null && (property == null || property.HasValue() == false)) + { + content = content.Parent; + property = content == null ? null : content.GetProperty(alias); + } + + return property; + } + + public object this[string alias] + { + get + { + var property = GetProperty(alias); + return property == null || property.HasValue() == false ? null : property.GetValue(); + } + } + + #endregion + } + + class SolidPublishedProperty : IPublishedProperty + { + public string Alias { get; set; } + public object SolidSourceValue { get; set; } + public object SolidValue { get; set; } + 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; + } + + [PublishedModel("ContentType2")] + internal class ContentType2 : PublishedContentModel + { + #region Plumbing + + public ContentType2(IPublishedContent content) + : base(content) + { } + + #endregion + + public int Prop1 => this.Value("prop1"); + } + + [PublishedModel("ContentType2Sub")] + internal class ContentType2Sub : ContentType2 + { + #region Plumbing + + public ContentType2Sub(IPublishedContent content) + : base(content) + { } + + #endregion + } + + class PublishedContentStrong1 : PublishedContentModel + { + public PublishedContentStrong1(IPublishedContent content) + : base(content) + { } + + public int StrongValue => this.Value("strongValue"); + } + + class PublishedContentStrong1Sub : PublishedContentStrong1 + { + public PublishedContentStrong1Sub(IPublishedContent content) + : base(content) + { } + + public int AnotherValue => this.Value("anotherValue"); + } + + class PublishedContentStrong2 : PublishedContentModel + { + public PublishedContentStrong2(IPublishedContent content) + : base(content) + { } + + public int StrongValue => this.Value("strongValue"); + } + + class AutoPublishedContentType : PublishedContentType + { + private static readonly PublishedPropertyType Default; + + static AutoPublishedContentType() + { + var dataTypeService = new TestObjects.TestDataTypeService( + new DataType(new VoidEditor(Mock.Of())) { Id = 666 }); + + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); + Default = factory.CreatePropertyType("*", 666); + } + + public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) + : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.InvariantNeutral) + { } + + public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) + : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.InvariantNeutral) + { } + + public override PublishedPropertyType GetPropertyType(string alias) + { + var propertyType = base.GetPropertyType(alias); + return propertyType ?? Default; + } + } +} diff --git a/src/Umbraco.Tests/Routing/RoutesCacheTests.cs b/src/Umbraco.Tests/Routing/RoutesCacheTests.cs index 4b04ebb50d..68e80076dd 100644 --- a/src/Umbraco.Tests/Routing/RoutesCacheTests.cs +++ b/src/Umbraco.Tests/Routing/RoutesCacheTests.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Routing { //var routingContext = GetRoutingContext("/test", 1111); var umbracoContext = GetUmbracoContext("/test", 0); - var cache = umbracoContext.PublishedShapshot.Content as PublishedContentCache; + var cache = umbracoContext.PublishedSnapshot.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); // fixme not sure? diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 23d3c84bc9..818e1f6817 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; @@ -99,7 +100,7 @@ namespace Umbraco.Tests.Routing [TestCase(1178, "/home/sub1/custom-sub-2/")] [TestCase(1175, "/home/sub-2/")] [TestCase(1172, "/test-page/")] - public void Get_Nice_Url_Not_Hiding_Top_Level(int nodeId, string niceUrlMatch) + public void Get_Url_Not_Hiding_Top_Level(int nodeId, string niceUrlMatch) { var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); @@ -129,7 +130,7 @@ namespace Umbraco.Tests.Routing [TestCase(1178, "/sub1/custom-sub-2/")] [TestCase(1175, "/sub-2/")] [TestCase(1172, "/test-page/")] // not hidden because not first root - public void Get_Nice_Url_Hiding_Top_Level(int nodeId, string niceUrlMatch) + public void Get_Url_Hiding_Top_Level(int nodeId, string niceUrlMatch) { var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); @@ -176,7 +177,7 @@ namespace Umbraco.Tests.Routing } [Test] - public void Get_Nice_Url_Unpublished() + public void Get_Url_Unpublished() { var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index 4f4e35f7e2..b064e9685c 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -171,7 +171,7 @@ namespace Umbraco.Tests.Routing [TestCase(10011, "http://domain2.com", false, "http://domain1.com/1001-1/")] [TestCase(1001, "https://domain1.com", false, "/")] [TestCase(10011, "https://domain1.com", false, "/1001-1/")] - public void Get_Nice_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, string expected) + public void Get_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, string expected) { var settings = SettingsForTests.GenerateMockUmbracoSettings(); var request = Mock.Get(settings.RequestHandler); @@ -207,7 +207,7 @@ namespace Umbraco.Tests.Routing [TestCase(10011, "http://domain2.com", false, "http://domain1.com/foo/1001-1/")] [TestCase(1001, "https://domain1.com", false, "http://domain1.com/foo/")] [TestCase(10011, "https://domain1.com", false, "http://domain1.com/foo/1001-1/")] - public void Get_Nice_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool absolute, string expected) + public void Get_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool absolute, string expected) { var settings = SettingsForTests.GenerateMockUmbracoSettings(); var request = Mock.Get(settings.RequestHandler); @@ -235,7 +235,7 @@ namespace Umbraco.Tests.Routing [TestCase(10011, "http://domain1.com", false, "/")] [TestCase(100111, "http://domain1.com", false, "/1001-1-1/")] [TestCase(1002, "http://domain1.com", false, "/1002/")] - public void Get_Nice_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, string expected) + public void Get_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, string expected) { var settings = SettingsForTests.GenerateMockUmbracoSettings(); var request = Mock.Get(settings.RequestHandler); @@ -269,7 +269,7 @@ namespace Umbraco.Tests.Routing [TestCase(1003, "http://domain3.com", false, "/")] [TestCase(10031, "http://domain3.com", false, "/en/")] [TestCase(100321, "http://domain3.com", false, "/fr/1003-2-1/")] - public void Get_Nice_Url_NestedDomains(int nodeId, string currentUrl, bool absolute, string expected) + public void Get_Url_NestedDomains(int nodeId, string currentUrl, bool absolute, string expected) { var settings = SettingsForTests.GenerateMockUmbracoSettings(); var request = Mock.Get(settings.RequestHandler); @@ -293,7 +293,7 @@ namespace Umbraco.Tests.Routing } [Test] - public void Get_Nice_Url_DomainsAndCache() + public void Get_Url_DomainsAndCache() { var settings = SettingsForTests.GenerateMockUmbracoSettings(); var request = Mock.Get(settings.RequestHandler); @@ -360,7 +360,7 @@ namespace Umbraco.Tests.Routing } [Test] - public void Get_Nice_Url_Relative_Or_Absolute() + public void Get_Url_Relative_Or_Absolute() { var settings = SettingsForTests.GenerateMockUmbracoSettings(); var requestMock = Mock.Get(settings.RequestHandler); @@ -394,7 +394,7 @@ namespace Umbraco.Tests.Routing } [Test] - public void Get_Nice_Url_Alternate() + public void Get_Url_Alternate() { var settings = SettingsForTests.GenerateMockUmbracoSettings(); diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 96404b1e59..97d88a680b 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -131,7 +131,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting webSecurity.Setup(x => x.UserHasSectionAccess(It.IsAny(), It.IsAny())) .Returns(() => true); - var publishedSnapshot = new Mock(); + var publishedSnapshot = new Mock(); publishedSnapshot.Setup(x => x.Members).Returns(Mock.Of()); var publishedSnapshotService = new Mock(); publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot.Object); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs index 10c8739029..3768803de2 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs @@ -4,6 +4,6 @@ namespace Umbraco.Tests.TestHelpers.Stubs { public class TestPublishedSnapshotAccessor : IPublishedSnapshotAccessor { - public IPublishedShapshot PublishedSnapshot { get; set; } + public IPublishedSnapshot PublishedSnapshot { get; set; } } } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 050c10757a..ee0dd38c45 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -106,7 +106,7 @@ namespace Umbraco.Tests.TestHelpers { var httpContext = Mock.Of(); - var publishedSnapshotMock = new Mock(); + var publishedSnapshotMock = new Mock(); publishedSnapshotMock.Setup(x => x.Members).Returns(Mock.Of()); var publishedSnapshot = publishedSnapshotMock.Object; var publishedSnapshotServiceMock = new Mock(); diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 2b6dcffeab..80a4da3fb5 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -343,20 +343,23 @@ namespace Umbraco.Tests.TestHelpers } } - protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null, IGlobalSettings globalSettings = null) + protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null) { // ensure we have a PublishedCachesService - var service = PublishedSnapshotService as PublishedSnapshotService; + var service = snapshotService ?? PublishedSnapshotService as PublishedSnapshotService; if (service == null) throw new Exception("Not a proper XmlPublishedCache.PublishedCachesService."); - // re-initialize PublishedCacheService content with an Xml source with proper template id - service.XmlStore.GetXmlDocument = () => + if (service is PublishedSnapshotService) { - var doc = new XmlDocument(); - doc.LoadXml(GetXmlContent(templateId)); - return doc; - }; + // re-initialize PublishedCacheService content with an Xml source with proper template id + ((PublishedSnapshotService)service).XmlStore.GetXmlDocument = () => + { + var doc = new XmlDocument(); + doc.LoadXml(GetXmlContent(templateId)); + return doc; + }; + } var httpContext = GetHttpContextFactory(url, routeData).HttpContext; diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 863b875394..655dac8796 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -128,6 +128,7 @@ + @@ -375,7 +376,6 @@ - diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 927b5557bd..931cc57493 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -100,7 +100,7 @@ namespace Umbraco.Tests.Web.Mvc [Test] public void Can_Lookup_Content() { - var publishedSnapshot = new Mock(); + var publishedSnapshot = new Mock(); publishedSnapshot.Setup(x => x.Members).Returns(Mock.Of()); var publishedSnapshotService = new Mock(); publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot.Object); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 9fe3b44264..afc461c6de 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -432,7 +432,7 @@ namespace Umbraco.Tests.Web.Mvc var globalSettings = TestObjects.GetGlobalSettings(); var ctx = new UmbracoContext( - GetHttpContextFactory(url, routeData).HttpContext, + http, _service, new WebSecurity(http, Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 5ca7b60cf0..d2a1ee6f80 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -98,7 +98,7 @@ namespace Umbraco.Web.Composing public static DistributedCache DistributedCache => Container.GetInstance(); - public static IPublishedShapshot PublishedSnapshot + public static IPublishedSnapshot PublishedSnapshot => Container.GetInstance().PublishedSnapshot; public static EventMessages EventMessages diff --git a/src/Umbraco.Web/PublishedCache/IPublishedShapshot.cs b/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs similarity index 97% rename from src/Umbraco.Web/PublishedCache/IPublishedShapshot.cs rename to src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs index 6e859d0f02..628a67b561 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedShapshot.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs @@ -8,7 +8,7 @@ namespace Umbraco.Web.PublishedCache /// /// A published snapshot is a point-in-time capture of the current state of /// everything that is "published". - public interface IPublishedShapshot + public interface IPublishedSnapshot { /// /// Gets the . diff --git a/src/Umbraco.Web/PublishedCache/IPublishedSnapshotAccessor.cs b/src/Umbraco.Web/PublishedCache/IPublishedSnapshotAccessor.cs index 9c0bc5a1e7..775de214ec 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedSnapshotAccessor.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedSnapshotAccessor.cs @@ -1,10 +1,10 @@ namespace Umbraco.Web.PublishedCache { /// - /// Provides access to the "current" . + /// Provides access to the "current" . /// public interface IPublishedSnapshotAccessor { - IPublishedShapshot PublishedSnapshot { get; set; } + IPublishedSnapshot PublishedSnapshot { get; set; } } } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/IPublishedSnapshotService.cs index 40c3538ed9..cf10ed5f3a 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedSnapshotService.cs @@ -6,7 +6,7 @@ using Umbraco.Web.Cache; namespace Umbraco.Web.PublishedCache { /// - /// Creates and manages instances. + /// Creates and manages instances. /// public interface IPublishedSnapshotService : IDisposable { @@ -32,7 +32,7 @@ namespace Umbraco.Web.PublishedCache /// If is null, the snapshot is not previewing, else it /// is previewing, and what is or is not visible in preview depends on the content of the token, /// which is not specified and depends on the actual published snapshot service implementation. - IPublishedShapshot CreatePublishedSnapshot(string previewToken); + IPublishedSnapshot CreatePublishedSnapshot(string previewToken); /// /// Gets the published snapshot accessor. diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 7fe898b080..f2e3355750 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -98,7 +98,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private CacheValues GetCacheValues(PropertyCacheLevel cacheLevel) { CacheValues cacheValues; - PublishedShapshot publishedSnapshot; + PublishedSnapshot publishedSnapshot; ICacheProvider cache; switch (cacheLevel) { @@ -115,7 +115,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // elements cache (if we don't want to pollute the elements cache with short-lived // data) depending on settings // for members, always cache in the snapshot cache - never pollute elements cache - publishedSnapshot = (PublishedShapshot) _publishedSnapshotAccessor.PublishedSnapshot; + publishedSnapshot = (PublishedSnapshot) _publishedSnapshotAccessor.PublishedSnapshot; cache = publishedSnapshot == null ? null : ((_isPreviewing == false || PublishedSnapshotService.FullCacheWhenPreviewing) && (_isMember == false) @@ -125,7 +125,7 @@ namespace Umbraco.Web.PublishedCache.NuCache break; case PropertyCacheLevel.Snapshot: // cache within the snapshot cache - publishedSnapshot = (PublishedShapshot) _publishedSnapshotAccessor.PublishedSnapshot; + publishedSnapshot = (PublishedSnapshot) _publishedSnapshotAccessor.PublishedSnapshot; cache = publishedSnapshot?.SnapshotCache; cacheValues = GetCacheValues(cache); break; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 480c18201f..b5201716ac 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -102,10 +102,10 @@ namespace Umbraco.Web.PublishedCache.NuCache // this is for tests purposes // args are: current published snapshot (may be null), previewing, content id - returns: content - internal static Func GetContentByIdFunc { get; set; } + internal static Func GetContentByIdFunc { get; set; } = (publishedShapshot, previewing, id) => publishedShapshot.Content.GetById(previewing, id); - internal static Func GetMediaByIdFunc { get; set; } + internal static Func GetMediaByIdFunc { get; set; } = (publishedShapshot, previewing, id) => publishedShapshot.Media.GetById(previewing, id); private IPublishedContent GetContentById(bool previewing, int id) @@ -289,7 +289,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // beware what you use that one for - you don't want to cache its result private ICacheProvider GetAppropriateCache() { - var publishedSnapshot = (PublishedShapshot)_publishedSnapshotAccessor.PublishedSnapshot; + var publishedSnapshot = (PublishedSnapshot)_publishedSnapshotAccessor.PublishedSnapshot; var cache = publishedSnapshot == null ? null : ((IsPreviewing == false || PublishedSnapshotService.FullCacheWhenPreviewing) && (ItemType != PublishedItemType.Member) @@ -300,7 +300,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private ICacheProvider GetCurrentSnapshotCache() { - var publishedSnapshot = (PublishedShapshot)_publishedSnapshotAccessor.PublishedSnapshot; + var publishedSnapshot = (PublishedSnapshot)_publishedSnapshotAccessor.PublishedSnapshot; return publishedSnapshot?.SnapshotCache; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedShapshot.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs similarity index 88% rename from src/Umbraco.Web/PublishedCache/NuCache/PublishedShapshot.cs rename to src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs index b21834f7b4..741e4d09f5 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedShapshot.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Cache; namespace Umbraco.Web.PublishedCache.NuCache { // implements published snapshot - internal class PublishedShapshot : IPublishedShapshot, IDisposable + internal class PublishedSnapshot : IPublishedSnapshot, IDisposable { private readonly PublishedSnapshotService _service; private bool _defaultPreview; @@ -13,7 +13,7 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Constructors - public PublishedShapshot(PublishedSnapshotService service, bool defaultPreview) + public PublishedSnapshot(PublishedSnapshotService service, bool defaultPreview) { _service = service; _defaultPreview = defaultPreview; @@ -72,13 +72,13 @@ namespace Umbraco.Web.PublishedCache.NuCache private class ForcedPreviewObject : DisposableObject { - private readonly PublishedShapshot _publishedShapshot; + private readonly PublishedSnapshot _publishedSnapshot; private readonly bool _origPreview; private readonly Action _callback; - public ForcedPreviewObject(PublishedShapshot publishedShapshot, bool preview, Action callback) + public ForcedPreviewObject(PublishedSnapshot publishedShapshot, bool preview, Action callback) { - _publishedShapshot = publishedShapshot; + _publishedSnapshot = publishedShapshot; _callback = callback; // save and force @@ -89,7 +89,7 @@ namespace Umbraco.Web.PublishedCache.NuCache protected override void DisposeResources() { // restore - _publishedShapshot._defaultPreview = _origPreview; + _publishedSnapshot._defaultPreview = _origPreview; _callback?.Invoke(_origPreview); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 1d83dcaae3..e611a738e5 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -533,7 +533,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } if (draftChanged || publishedChanged) - ((PublishedShapshot)CurrentPublishedShapshot).Resync(); + ((PublishedSnapshot)CurrentPublishedSnapshot).Resync(); } private void NotifyLocked(IEnumerable payloads, out bool draftChanged, out bool publishedChanged) @@ -630,7 +630,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } if (anythingChanged) - ((PublishedShapshot)CurrentPublishedShapshot).Resync(); + ((PublishedSnapshot)CurrentPublishedSnapshot).Resync(); } private void NotifyLocked(IEnumerable payloads, out bool anythingChanged) @@ -719,7 +719,7 @@ namespace Umbraco.Web.PublishedCache.NuCache Notify(_contentStore, payloads, RefreshContentTypesLocked); Notify(_mediaStore, payloads, RefreshMediaTypesLocked); - ((PublishedShapshot)CurrentPublishedShapshot).Resync(); + ((PublishedSnapshot)CurrentPublishedSnapshot).Resync(); } private void Notify(ContentStore store, ContentTypeCacheRefresher.JsonPayload[] payloads, Action, IEnumerable, IEnumerable, IEnumerable> action) @@ -796,7 +796,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - ((PublishedShapshot)CurrentPublishedShapshot).Resync(); + ((PublishedSnapshot)CurrentPublishedSnapshot).Resync(); } public override void Notify(DomainCacheRefresher.JsonPayload[] payloads) @@ -945,17 +945,17 @@ namespace Umbraco.Web.PublishedCache.NuCache private long _contentGen, _mediaGen, _domainGen; private ICacheProvider _elementsCache; - public override IPublishedShapshot CreatePublishedSnapshot(string previewToken) + public override IPublishedSnapshot CreatePublishedSnapshot(string previewToken) { // no cache, no joy if (_isReady == false) throw new InvalidOperationException("The published snapshot service has not properly initialized."); var preview = previewToken.IsNullOrWhiteSpace() == false; - return new PublishedShapshot(this, preview); + return new PublishedSnapshot(this, preview); } - public PublishedShapshot.PublishedSnapshotElements GetElements(bool previewDefault) + public PublishedSnapshot.PublishedSnapshotElements GetElements(bool previewDefault) { // note: using ObjectCacheRuntimeCacheProvider for elements and snapshot caches // is not recommended because it creates an inner MemoryCache which is a heavy @@ -996,7 +996,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // a MaxValue to make sure this one runs last, and it should be ok scopeContext.Enlist("Umbraco.Web.PublishedCache.NuCache.PublishedSnapshotService.Resync", () => this, (completed, svc) => { - ((PublishedShapshot)svc.CurrentPublishedShapshot).Resync(); + ((PublishedSnapshot)svc.CurrentPublishedSnapshot).Resync(); }, int.MaxValue); } @@ -1017,7 +1017,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var domainCache = new DomainCache(domainSnap); var domainHelper = new DomainHelper(domainCache, _siteDomainHelper); - return new PublishedShapshot.PublishedSnapshotElements + return new PublishedSnapshot.PublishedSnapshotElements { ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainHelper, _globalSettings, _serviceContext.LocalizationService), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), diff --git a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs index 08a754153c..685c129224 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs @@ -15,9 +15,9 @@ namespace Umbraco.Web.PublishedCache // note: NOT setting _publishedSnapshotAccessor.PublishedSnapshot here because it is the // responsibility of the caller to manage what the 'current' facade is - public abstract IPublishedShapshot CreatePublishedSnapshot(string previewToken); + public abstract IPublishedSnapshot CreatePublishedSnapshot(string previewToken); - protected IPublishedShapshot CurrentPublishedShapshot => PublishedSnapshotAccessor.PublishedSnapshot; + protected IPublishedSnapshot CurrentPublishedSnapshot => PublishedSnapshotAccessor.PublishedSnapshot; public abstract bool EnsureEnvironment(out IEnumerable errors); diff --git a/src/Umbraco.Web/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs b/src/Umbraco.Web/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs index cc4b09f657..706d67020a 100644 --- a/src/Umbraco.Web/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs +++ b/src/Umbraco.Web/PublishedCache/UmbracoContextPublishedSnapshotAccessor.cs @@ -11,13 +11,13 @@ namespace Umbraco.Web.PublishedCache _umbracoContextAccessor = umbracoContextAccessor; } - public IPublishedShapshot PublishedSnapshot + public IPublishedSnapshot PublishedSnapshot { get { var umbracoContext = _umbracoContextAccessor.UmbracoContext; if (umbracoContext == null) throw new Exception("The IUmbracoContextAccessor could not provide an UmbracoContext."); - return umbracoContext.PublishedShapshot; + return umbracoContext.PublishedSnapshot; } set diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedShapshot.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshot.cs similarity index 92% rename from src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedShapshot.cs rename to src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshot.cs index e0417bdebf..61ea7d2534 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedShapshot.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshot.cs @@ -7,13 +7,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// /// Implements a published snapshot. /// - class PublishedShapshot : IPublishedShapshot + class PublishedSnapshot : IPublishedSnapshot { /// - /// Initializes a new instance of the class with a content cache + /// Initializes a new instance of the class with a content cache /// and a media cache. /// - public PublishedShapshot( + public PublishedSnapshot( PublishedContentCache contentCache, PublishedMediaCache mediaCache, PublishedMemberCache memberCache, diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs index 7cde462dbc..1f327f4cd3 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs @@ -139,7 +139,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache #region Caches - public override IPublishedShapshot CreatePublishedSnapshot(string previewToken) + public override IPublishedSnapshot CreatePublishedSnapshot(string previewToken) { // use _requestCache to store recursive properties lookup, etc. both in content // and media cache. Life span should be the current request. Or, ideally @@ -148,7 +148,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache var domainCache = new DomainCache(_domainService, _localizationService); - return new PublishedShapshot( + return new PublishedSnapshot( new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _siteDomainHelper, _contentTypeCache, _routesCache, previewToken), new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache), new PublishedMemberCache(_xmlStore, _requestCache, _memberService, _contentTypeCache), diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs index 60b5aa1643..85deb72066 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs @@ -1250,7 +1250,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; private void ResyncCurrentPublishedSnapshot(XmlDocument xml) { - var publishedSnapshot = (PublishedShapshot) _publishedSnapshotAccessor.PublishedSnapshot; + var publishedSnapshot = (PublishedSnapshot) _publishedSnapshotAccessor.PublishedSnapshot; if (publishedSnapshot == null) return; ((PublishedContentCache) publishedSnapshot.Content).Resync(xml); ((PublishedMediaCache) publishedSnapshot.Media).Resync(); diff --git a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs index 6cc3b74972..8ffde220c2 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs @@ -54,7 +54,7 @@ namespace Umbraco.Web.Routing } if (node != null) { - var d = DomainHelper.FindWildcardDomainInPath(frequest.UmbracoContext.PublishedShapshot.Domains.GetAll(true), node.Path, null); + var d = DomainHelper.FindWildcardDomainInPath(frequest.UmbracoContext.PublishedSnapshot.Domains.GetAll(true), node.Path, null); if (d != null) errorCulture = d.Culture; } diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index ac07f1ad32..0b4fc89b6b 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -266,7 +266,7 @@ namespace Umbraco.Web.Routing _logger.Debug(() => $"{tracePrefix}Uri=\"{request.Uri}\""); // try to find a domain matching the current request - var domainAndUri = DomainHelper.DomainForUri(request.UmbracoContext.PublishedShapshot.Domains.GetAll(false), request.Uri); + var domainAndUri = DomainHelper.DomainForUri(request.UmbracoContext.PublishedSnapshot.Domains.GetAll(false), request.Uri); // handle domain - always has a contentId and a culture if (domainAndUri != null) @@ -311,7 +311,7 @@ namespace Umbraco.Web.Routing var nodePath = request.PublishedContent.Path; _logger.Debug(() => $"{tracePrefix}Path=\"{nodePath}\""); var rootNodeId = request.HasDomain ? request.Domain.ContentId : (int?)null; - var domain = DomainHelper.FindWildcardDomainInPath(request.UmbracoContext.PublishedShapshot.Domains.GetAll(true), nodePath, rootNodeId); + var domain = DomainHelper.FindWildcardDomainInPath(request.UmbracoContext.PublishedSnapshot.Domains.GetAll(true), nodePath, rootNodeId); // always has a contentId and a culture if (domain != null) @@ -602,14 +602,14 @@ namespace Umbraco.Web.Routing var loginPageId = publicAccessAttempt.Result.LoginNodeId; if (loginPageId != request.PublishedContent.Id) - request.PublishedContent = request.UmbracoContext.PublishedShapshot.Content.GetById(loginPageId); + request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(loginPageId); } else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, GetRolesForLogin(membershipHelper.CurrentUserName)) == false) { _logger.Debug(() => $"{tracePrefix}Current member has not access, redirect to error page"); var errorPageId = publicAccessAttempt.Result.NoAccessNodeId; if (errorPageId != request.PublishedContent.Id) - request.PublishedContent = request.UmbracoContext.PublishedShapshot.Content.GetById(errorPageId); + request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(errorPageId); } else { diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index 5e9a4efc41..9b7ce72817 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -94,7 +94,7 @@ namespace Umbraco.Web.Security _umbracoContext = umbracoContext; _membershipProvider = membershipProvider; _roleProvider = roleProvider; - _memberCache = umbracoContext.PublishedShapshot.Members; + _memberCache = umbracoContext.PublishedSnapshot.Members; // helpers are *not* instanciated by the container so we have to // get our dependencies injected manually, through properties. diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 44bc3ec324..1339166185 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -342,6 +342,7 @@ + @@ -351,13 +352,13 @@ + - @@ -371,7 +372,6 @@ - @@ -389,7 +389,7 @@ - + diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index ef684a21ba..c58ff40575 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web public class UmbracoContext : DisposableObject, IDisposeOnRequestEnd { private readonly IGlobalSettings _globalSettings; - private readonly Lazy _publishedSnapshot; + private readonly Lazy _publishedSnapshot; private DomainHelper _domainHelper; private string _previewToken; private bool? _previewing; @@ -125,7 +125,7 @@ namespace Umbraco.Web Security = webSecurity; // beware - we cannot expect a current user here, so detecting preview mode must be a lazy thing - _publishedSnapshot = new Lazy(() => publishedSnapshotService.CreatePublishedSnapshot(PreviewToken)); + _publishedSnapshot = new Lazy(() => publishedSnapshotService.CreatePublishedSnapshot(PreviewToken)); // set the urls... // NOTE: The request will not be available during app startup so we can only set this to an absolute URL of localhost, this @@ -178,7 +178,7 @@ namespace Umbraco.Web /// /// Gets the published snapshot. /// - public IPublishedShapshot PublishedShapshot => _publishedSnapshot.Value; + public IPublishedSnapshot PublishedSnapshot => _publishedSnapshot.Value; // for unit tests internal bool HasPublishedSnapshot => _publishedSnapshot.IsValueCreated; @@ -186,12 +186,12 @@ namespace Umbraco.Web /// /// Gets the published content cache. /// - public IPublishedContentCache ContentCache => PublishedShapshot.Content; + public IPublishedContentCache ContentCache => PublishedSnapshot.Content; /// /// Gets the published media cache. /// - public IPublishedMediaCache MediaCache => PublishedShapshot.Media; + public IPublishedMediaCache MediaCache => PublishedSnapshot.Media; /// /// Boolean value indicating whether the current request is a front-end umbraco request @@ -227,7 +227,7 @@ namespace Umbraco.Web internal DomainHelper GetDomainHelper(ISiteDomainHelper siteDomainHelper) { if (_domainHelper == null) - _domainHelper = new DomainHelper(PublishedShapshot.Domains, siteDomainHelper); + _domainHelper = new DomainHelper(PublishedSnapshot.Domains, siteDomainHelper); return _domainHelper; } @@ -314,7 +314,7 @@ namespace Umbraco.Web internal IDisposable ForcedPreview(bool preview) { InPreviewMode = preview; - return PublishedShapshot.ForcedPreview(preview, orig => InPreviewMode = orig); + return PublishedSnapshot.ForcedPreview(preview, orig => InPreviewMode = orig); } private HttpRequestBase GetRequestFromContext() From c19dbeda2320532be3ecaed1849228e6a84dbc0b Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Apr 2018 13:27:15 +1000 Subject: [PATCH 47/97] Adds a couple of tests which yielded some other issues with getting urls by culture, those are now fixed --- .../Published/ConvertersTests.cs | 7 +- .../Published/PublishedSnapshotTestObjects.cs | 64 +-------- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 126 ++++++++++++++++-- .../TestHelpers/Stubs/TestPublishedContent.cs | 66 +++++++++ .../TestHelpers/TestWithDatabaseBase.cs | 3 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../Mapping/RedirectUrlMapperProfile.cs | 10 +- .../PublishedCache/NuCache/ContentCache.cs | 4 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 13 +- src/Umbraco.Web/Routing/DomainHelper.cs | 24 ++-- src/Umbraco.Web/Routing/UrlProvider.cs | 4 +- 11 files changed, 226 insertions(+), 96 deletions(-) create mode 100644 src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index 0c47a551bc..25933fdd9d 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache; @@ -99,7 +100,7 @@ namespace Umbraco.Tests.Published var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); var cntType1 = contentTypeFactory.CreateContentType(1001, "cnt1", Array.Empty()); - var cnt1 = new PublishedSnapshotTestObjects.TestPublishedContent(cntType1, 1234, Guid.NewGuid(), new Dictionary(), false); + var cnt1 = new TestPublishedContent(cntType1, 1234, Guid.NewGuid(), new Dictionary(), false); cacheContent[cnt1.Id] = cnt1; Assert.AreSame(cnt1, element1.Value("prop1")); @@ -200,8 +201,8 @@ namespace Umbraco.Tests.Published var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); var element2 = new PublishedElement(elementType2, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); - var cnt1 = new PublishedSnapshotTestObjects.TestPublishedContent(contentType1, 1003, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); - var cnt2 = new PublishedSnapshotTestObjects.TestPublishedContent(contentType2, 1004, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); + var cnt1 = new TestPublishedContent(contentType1, 1003, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); + var cnt2 = new TestPublishedContent(contentType2, 1004, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); cacheContent[cnt1.Id] = cnt1.CreateModel(); cacheContent[cnt2.Id] = cnt2.CreateModel(); diff --git a/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs b/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs index acfc12d408..fcb462e5c5 100644 --- a/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs +++ b/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs @@ -8,8 +8,6 @@ namespace Umbraco.Tests.Published { public class PublishedSnapshotTestObjects { - #region Published models - [PublishedModel("element1")] public class TestElementModel1 : PublishedElementModel { @@ -49,66 +47,6 @@ namespace Umbraco.Tests.Published public IEnumerable Prop2 => this.Value>("prop2"); } - - #endregion - - #region Support classes - - internal class TestPublishedContent : PublishedElement, IPublishedContent - { - public TestPublishedContent(PublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing) - : base(contentType, key, values, previewing) - { - Id = id; - } - - public int Id { get; } - public int TemplateId { get; set; } - public int SortOrder { get; set; } - public string Name { get; set; } - public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); - public string UrlName { get; set; } - public string DocumentTypeAlias => ContentType.Alias; - public int DocumentTypeId { get; set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - public string Url { get; set; } - public PublishedItemType ItemType => ContentType.ItemType; - public bool IsDraft { get; set; } - public IPublishedContent Parent { get; set; } - public IEnumerable Children { get; set; } - - // copied from PublishedContentBase - public IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - var firstNonNullProperty = property; - while (content != null && (property == null || property.HasValue() == false)) - { - content = content.Parent; - property = content?.GetProperty(alias); - if (firstNonNullProperty == null && property != null) firstNonNullProperty = property; - } - - // if we find a content with the property with a value, return that property - // if we find no content with the property, return null - // if we find a content with the property without a value, return that property - // have to save that first property while we look further up, hence firstNonNullProperty - - return property != null && property.HasValue() ? property : firstNonNullProperty; - } - } - - #endregion + } } diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 818e1f6817..070c68468a 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -1,9 +1,13 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.XmlPublishedCache; @@ -44,11 +48,11 @@ namespace Umbraco.Tests.Routing globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettings.Object); - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new [] + var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] { new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); - + }, globalSettings: globalSettings.Object); + var requestHandlerMock = Mock.Get(_umbracoSettings.RequestHandler); requestHandlerMock.Setup(x => x.AddTrailingSlash).Returns(false);// (cached routes have none) @@ -110,7 +114,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] { new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + }, globalSettings: globalSettings.Object); var requestMock = Mock.Get(_umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); @@ -140,7 +144,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] { new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + }, globalSettings: globalSettings.Object); var requestMock = Mock.Get(_umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); @@ -149,14 +153,118 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(niceUrlMatch, result); } + /// + /// This tests DefaultUrlProvider.GetUrl with a specific culture when the current URL is the culture specific domain + /// [Test] - public void Get_Nice_Url_Relative_Or_Absolute() + public void Get_Url_For_Culture_Variant_With_Current_Url() + { + const string currentUri = "http://example.com/fr/test"; + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var publishedContentCache = new Mock(); + publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) + .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard + + var domainCache = new Mock(); + domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) + .Returns((int contentId, bool includeWildcards) => + { + if (contentId != 9876) return Enumerable.Empty(); + return new[] + { + new Domain(2, "example.com/en", 9876, CultureInfo.GetCultureInfo("en-US"), false, true), //default + new Domain(3, "example.com/fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false, true) + }; + }); + + var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); + + var snapshotService = new Mock(); + snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) + .Returns(snapshot); + + var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: _umbracoSettings, + urlProviders: new[] { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, + globalSettings: globalSettings.Object, + snapshotService: snapshotService.Object); + + + var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); + + //the current uri is the culture specific domain we want, so the result is a relative path since we are on the culture specific domain + Assert.AreEqual("/fr/home/test-fr/", url); + } + + /// + /// This tests DefaultUrlProvider.GetUrl with a specific culture when the current URL is not the culture specific domain + /// + [Test] + public void Get_Url_For_Culture_Variant_Non_Current_Url() + { + const string currentUri = "http://example.com/en/test"; + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var publishedContentCache = new Mock(); + publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) + .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard + + var domainCache = new Mock(); + domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) + .Returns((int contentId, bool includeWildcards) => + { + if (contentId != 9876) return Enumerable.Empty(); + return new[] + { + new Domain(2, "example.com/en", 9876, CultureInfo.GetCultureInfo("en-US"), false, true), //default + new Domain(3, "example.com/fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false, true) + }; + }); + + var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); + + var snapshotService = new Mock(); + snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) + .Returns(snapshot); + + var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: _umbracoSettings, + urlProviders: new[] { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, + globalSettings: globalSettings.Object, + snapshotService: snapshotService.Object); + + + var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); + + //the current uri is not the culture specific domain we want, so the result is an absolute path to the culture specific domain + Assert.AreEqual("http://example.com/fr/home/test-fr/", url); + } + + [Test] + public void Get_Url_Relative_Or_Absolute() { var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettings.Object); - + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); @@ -164,7 +272,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, umbracoSettings: _umbracoSettings, urlProviders: new[] { new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + }, globalSettings: globalSettings.Object); Assert.AreEqual("/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); @@ -187,7 +295,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, urlProviders: new[] { new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) - }, globalSettings:globalSettings.Object); + }, globalSettings: globalSettings.Object); //mock the Umbraco settings that we need var requestMock = Mock.Get(_umbracoSettings.RequestHandler); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs new file mode 100644 index 0000000000..7d9bbec855 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.TestHelpers.Stubs +{ + internal class TestPublishedContent : PublishedElement, IPublishedContent + { + public TestPublishedContent(PublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultureNames = null) + : base(contentType, key, values, previewing) + { + Id = id; + CultureNames = cultureNames; + } + + public int Id { get; } + public int TemplateId { get; set; } + public int SortOrder { get; set; } + public string Name { get; set; } + public IReadOnlyDictionary CultureNames { get; set; } + public string UrlName { get; set; } + public string DocumentTypeAlias => ContentType.Alias; + public int DocumentTypeId { get; set; } + public string WriterName { get; set; } + public string CreatorName { get; set; } + public int WriterId { get; set; } + public int CreatorId { get; set; } + public string Path { get; set; } + public DateTime CreateDate { get; set; } + public DateTime UpdateDate { get; set; } + public Guid Version { get; set; } + public int Level { get; set; } + public string Url { get; set; } + public PublishedItemType ItemType => ContentType.ItemType; + public bool IsDraft { get; set; } + public IPublishedContent Parent { get; set; } + public IEnumerable Children { get; set; } + + // copied from PublishedContentBase + public IPublishedProperty GetProperty(string alias, bool recurse) + { + var property = GetProperty(alias); + if (recurse == false) return property; + + IPublishedContent content = this; + var firstNonNullProperty = property; + while (content != null && (property == null || property.HasValue() == false)) + { + content = content.Parent; + property = content?.GetProperty(alias); + if (firstNonNullProperty == null && property != null) firstNonNullProperty = property; + } + + // if we find a content with the property with a value, return that property + // if we find no content with the property, return null + // if we find a content with the property without a value, return that property + // have to save that first property while we look further up, hence firstNonNullProperty + + return property != null && property.HasValue() ? property : firstNonNullProperty; + } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 80a4da3fb5..070166eaff 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -366,7 +366,8 @@ namespace Umbraco.Tests.TestHelpers var umbracoContext = new UmbracoContext( httpContext, service, - new WebSecurity(httpContext, Container.GetInstance(), Container.GetInstance()), + new WebSecurity(httpContext, Container.GetInstance(), + Container.GetInstance()), umbracoSettings ?? Container.GetInstance(), urlProviders ?? Enumerable.Empty(), globalSettings ?? Container.GetInstance(), diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 655dac8796..eaec8a24ff 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -178,6 +178,7 @@ + diff --git a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs index e92e72db77..33e2164a21 100644 --- a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs @@ -2,15 +2,23 @@ using Umbraco.Core.Models; using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Routing; namespace Umbraco.Web.Models.Mapping { internal class RedirectUrlMapperProfile : Profile { + private readonly UrlProvider _urlProvider; + + public RedirectUrlMapperProfile(UrlProvider urlProvider) + { + _urlProvider = urlProvider; + } + public RedirectUrlMapperProfile() { CreateMap() - .ForMember(x => x.OriginalUrl, expression => expression.MapFrom(item => Current.UmbracoContext.UrlProvider.GetUrlFromRoute(item.ContentId, item.Url))) + .ForMember(x => x.OriginalUrl, expression => expression.MapFrom(item => _urlProvider.GetUrlFromRoute(item.ContentId, item.Url, null))) .ForMember(x => x.DestinationUrl, expression => expression.Ignore()) .ForMember(x => x.RedirectId, expression => expression.MapFrom(item => item.Key)); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index fc95557dd1..37b8e97a28 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -163,7 +163,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // assemble the route pathParts.Reverse(); var path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc - var route = (n?.Id.ToString(CultureInfo.InvariantCulture) ?? "") + path; + //prefix the root node id containing the domain if it exists (this is a standard way of creating route paths) + //and is done so that we know the ID of the domain node for the path + var route = (n?.Id.ToString(CultureInfo.InvariantCulture) ?? "") + path; return route; } diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 788a7a809b..0ea0c950da 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web.Routing #region GetUrl /// - /// Gets the nice url of a published content. + /// Gets the url of a published content. /// /// The Umbraco context. /// The published content id. @@ -51,10 +51,10 @@ namespace Umbraco.Web.Routing // will not use cache if previewing var route = umbracoContext.ContentCache.GetRouteById(id, culture); - return GetUrlFromRoute(route, umbracoContext, id, current, mode); + return GetUrlFromRoute(route, umbracoContext, id, current, mode, culture); } - internal string GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode) + internal string GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture) { if (string.IsNullOrWhiteSpace(route)) { @@ -71,7 +71,7 @@ namespace Umbraco.Web.Routing var path = pos == 0 ? route : route.Substring(pos); var domainUri = pos == 0 ? null - : domainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current); + : domainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current, culture); // assemble the url from domainUri (maybe null) and path return AssembleUrl(domainUri, path, current, mode).ToString(); @@ -119,8 +119,7 @@ namespace Umbraco.Web.Routing var route = umbracoContext.ContentCache.GetRouteById(id, d?.Culture?.Name); if (route == null) continue; - //need to strip off the leading ID for the route - //TODO: Is there a nicer way to deal with this? + //need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned) var pos = route.IndexOf('/'); var path = pos == 0 ? route : route.Substring(pos); @@ -170,7 +169,7 @@ namespace Umbraco.Web.Routing { if (mode == UrlProviderMode.Auto) { - if (current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority)) + if (current != null && current.GetLeftPart(UriPartial.Path).InvariantStartsWith(domainUri.Uri.GetLeftPart(UriPartial.Path))) mode = UrlProviderMode.Relative; else mode = UrlProviderMode.Absolute; diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index ba7b59323b..db051c20c5 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -31,7 +31,7 @@ namespace Umbraco.Web.Routing /// The domain and its uri, if any, that best matches the specified uri, else null. /// If at least a domain is set on the node then the method returns the domain that /// best matches the specified uri, else it returns null. - internal DomainAndUri DomainForNode(int nodeId, Uri current) + internal DomainAndUri DomainForNode(int nodeId, Uri current, string culture = null) { // be safe if (nodeId <= 0) @@ -45,7 +45,7 @@ namespace Umbraco.Web.Routing return null; // else filter - var domainAndUri = DomainForUri(domains, current, domainAndUris => _siteDomainHelper.MapDomain(current, domainAndUris)); + var domainAndUri = DomainForUri(domains, current, culture, domainAndUris => _siteDomainHelper.MapDomain(current, domainAndUris)); if (domainAndUri == null) throw new Exception("DomainForUri returned null."); @@ -108,14 +108,13 @@ namespace Umbraco.Web.Routing /// the right one, unless it is null, in which case the method returns null. /// The filter, if any, will be called only with a non-empty argument, and _must_ return something. /// - internal static DomainAndUri DomainForUri(IEnumerable domains, Uri current, Func filter = null) + internal static DomainAndUri DomainForUri(IEnumerable domains, Uri current, string culture = null, Func filter = null) { // sanitize the list to have proper uris for comparison (scheme, path end with /) // we need to end with / because example.com/foo cannot match example.com/foobar // we need to order so example.com/foo matches before example.com/ var domainsAndUris = domains .Where(d => d.IsWildcard == false) - //.Select(SanitizeForBackwardCompatibility) .Select(d => new DomainAndUri(d, current)) .OrderByDescending(d => d.Uri.ToString()) .ToArray(); @@ -126,8 +125,12 @@ namespace Umbraco.Web.Routing DomainAndUri domainAndUri; if (current == null) { - //get the default domain (there should be one) - domainAndUri = domainsAndUris.FirstOrDefault(x => x.IsDefault); + //get the default domain or the one matching the culture if specified + domainAndUri = domainsAndUris.FirstOrDefault(x => culture.IsNullOrWhiteSpace() ? x.IsDefault : x.Culture.Name.InvariantEquals(culture)); + + if (domainAndUri == null && !culture.IsNullOrWhiteSpace()) + throw new InvalidOperationException($"No domain was found by the specified culture '{culture}'"); + if (domainAndUri == null) domainAndUri = domainsAndUris.First(); // take the first one by default (what else can we do?) } @@ -138,13 +141,17 @@ namespace Umbraco.Web.Routing var currentWithSlash = current.EndPathWithSlash(); domainAndUri = domainsAndUris .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash)); - if (domainAndUri != null) return domainAndUri; + //is culture specified? if so this will need to match too + if (domainAndUri != null && (culture.IsNullOrWhiteSpace() || domainAndUri.Culture.Name.InvariantEquals(culture))) + return domainAndUri; // if none matches, try again without the port // ie current is www.example.com:1234/foo/bar, look for domain www.example.com domainAndUri = domainsAndUris .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash.WithoutPort())); - if (domainAndUri != null) return domainAndUri; + //is culture specified? if so this will need to match too + if (domainAndUri != null && (culture.IsNullOrWhiteSpace() || domainAndUri.Culture.Name.InvariantEquals(culture))) + return domainAndUri; // if none matches, then try to run the filter to pick a domain if (filter != null) @@ -170,7 +177,6 @@ namespace Umbraco.Web.Routing { return domains .Where(d => d.IsWildcard == false) - //.Select(SanitizeForBackwardCompatibility) .Select(d => new DomainAndUri(d, current)) .OrderByDescending(d => d.Uri.ToString()); } diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 422fb4d4e7..98bd1b182d 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -216,12 +216,12 @@ namespace Umbraco.Web.Routing return url ?? "#"; // legacy wants this } - internal string GetUrlFromRoute(int id, string route) + internal string GetUrlFromRoute(int id, string route, string culture) { var provider = _urlProviders.OfType().FirstOrDefault(); var url = provider == null ? route // what else? - : provider.GetUrlFromRoute(route, UmbracoContext.Current, id, _umbracoContext.CleanedUmbracoUrl, Mode); + : provider.GetUrlFromRoute(route, UmbracoContext.Current, id, _umbracoContext.CleanedUmbracoUrl, Mode, culture); return url ?? "#"; } From 335cf656632dade7b0124691a31b04eec31d7e96 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Apr 2018 14:01:29 +1000 Subject: [PATCH 48/97] fixes more of my test logic --- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 17 ++++++------ src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 5 ++-- src/Umbraco.Web/Routing/DomainHelper.cs | 27 ++++++++++++------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 070c68468a..038211b86a 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -159,7 +159,7 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Url_For_Culture_Variant_With_Current_Url() { - const string currentUri = "http://example.com/fr/test"; + const string currentUri = "http://example.fr/test"; var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); @@ -180,8 +180,8 @@ namespace Umbraco.Tests.Routing if (contentId != 9876) return Enumerable.Empty(); return new[] { - new Domain(2, "example.com/en", 9876, CultureInfo.GetCultureInfo("en-US"), false, true), //default - new Domain(3, "example.com/fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false, true) + new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false, true), //default + new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false, true) }; }); @@ -201,8 +201,7 @@ namespace Umbraco.Tests.Routing var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); - //the current uri is the culture specific domain we want, so the result is a relative path since we are on the culture specific domain - Assert.AreEqual("/fr/home/test-fr/", url); + Assert.AreEqual("/home/test-fr/", url); } /// @@ -211,7 +210,7 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Url_For_Culture_Variant_Non_Current_Url() { - const string currentUri = "http://example.com/en/test"; + const string currentUri = "http://example.us/test"; var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); @@ -232,8 +231,8 @@ namespace Umbraco.Tests.Routing if (contentId != 9876) return Enumerable.Empty(); return new[] { - new Domain(2, "example.com/en", 9876, CultureInfo.GetCultureInfo("en-US"), false, true), //default - new Domain(3, "example.com/fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false, true) + new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false, true), //default + new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false, true) }; }); @@ -254,7 +253,7 @@ namespace Umbraco.Tests.Routing var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); //the current uri is not the culture specific domain we want, so the result is an absolute path to the culture specific domain - Assert.AreEqual("http://example.com/fr/home/test-fr/", url); + Assert.AreEqual("http://example.fr/home/test-fr/", url); } [Test] diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 0ea0c950da..6a01250221 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -168,8 +168,9 @@ namespace Umbraco.Web.Routing else // a domain was found { if (mode == UrlProviderMode.Auto) - { - if (current != null && current.GetLeftPart(UriPartial.Path).InvariantStartsWith(domainUri.Uri.GetLeftPart(UriPartial.Path))) + { + //this check is a little tricky, we can't just compare domains + if (current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority)) mode = UrlProviderMode.Relative; else mode = UrlProviderMode.Absolute; diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index db051c20c5..76a0845113 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -125,6 +125,8 @@ namespace Umbraco.Web.Routing DomainAndUri domainAndUri; if (current == null) { + //match either by culture (if specified) or default + //get the default domain or the one matching the culture if specified domainAndUri = domainsAndUris.FirstOrDefault(x => culture.IsNullOrWhiteSpace() ? x.IsDefault : x.Culture.Name.InvariantEquals(culture)); @@ -133,25 +135,32 @@ namespace Umbraco.Web.Routing if (domainAndUri == null) domainAndUri = domainsAndUris.First(); // take the first one by default (what else can we do?) + } + else if (!culture.IsNullOrWhiteSpace()) + { + //match by culture + + domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture)); + if (domainAndUri == null) + throw new InvalidOperationException($"No domain was found by the specified culture '{culture}'"); + return domainAndUri; } else - { + { + //try to match by current, else filter + // look for the first domain that would be the base of the current url // ie current is www.example.com/foo/bar, look for domain www.example.com var currentWithSlash = current.EndPathWithSlash(); domainAndUri = domainsAndUris - .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash)); - //is culture specified? if so this will need to match too - if (domainAndUri != null && (culture.IsNullOrWhiteSpace() || domainAndUri.Culture.Name.InvariantEquals(culture))) - return domainAndUri; + .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash)); + if (domainAndUri != null) return domainAndUri; // if none matches, try again without the port // ie current is www.example.com:1234/foo/bar, look for domain www.example.com domainAndUri = domainsAndUris - .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash.WithoutPort())); - //is culture specified? if so this will need to match too - if (domainAndUri != null && (culture.IsNullOrWhiteSpace() || domainAndUri.Culture.Name.InvariantEquals(culture))) - return domainAndUri; + .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash.WithoutPort())); + if (domainAndUri != null) return domainAndUri; // if none matches, then try to run the filter to pick a domain if (filter != null) From f6984438a01fb8a25b95c2d7b13e3093d80461ae Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Apr 2018 15:26:50 +1000 Subject: [PATCH 49/97] Fixes cases where there are no domains assigned, ensures there can't be duplicate URLs returned to the UI --- .../PublishedCache/NuCache/ContentCache.cs | 6 ++-- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 9 ++++-- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 32 ++++++++++--------- .../Routing/UrlProviderExtensions.cs | 9 ++++-- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 37b8e97a28..c7661a3f28 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -104,8 +104,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // hideTopLevelNode = support legacy stuff, look for /*/path/to/node // else normal, look for /path/to/node content = hideTopLevelNode.Value - ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.UrlName == parts[0]) - : GetAtRoot(preview).FirstOrDefault(x => x.UrlName == parts[0]); + ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.GetUrlName(_localizationService, culture) == parts[0]) + : GetAtRoot(preview).FirstOrDefault(x => x.GetUrlName(_localizationService, culture) == parts[0]); content = FollowRoute(content, parts, 1, culture); } @@ -114,7 +114,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // have to look for /foo (see note in ApplyHideTopLevelNodeFromPath). if (content == null && hideTopLevelNode.Value && parts.Length == 1) { - content = GetAtRoot(preview).FirstOrDefault(x => x.UrlName == parts[0]); + content = GetAtRoot(preview).FirstOrDefault(x => x.GetUrlName(_localizationService, culture) == parts[0]); } return content; diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 5e3f8ea919..4b7cb48add 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -90,8 +90,12 @@ namespace Umbraco.Web.Routing if (domainUris == null) { - var path = "/" + node.Value(Constants.Conventions.Content.UrlAlias); - var uri = new Uri(path, UriKind.Relative); + var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias); + if (string.IsNullOrWhiteSpace(umbracoUrlName)) + return Enumerable.Empty(); + + var path = "/" + umbracoUrlName; + var uri = new Uri(path, UriKind.Relative); return new[] { UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString() }; } else @@ -107,7 +111,6 @@ namespace Umbraco.Web.Routing result.Add(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString()); } } - return result; } } diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 6a01250221..e09f343555 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -94,23 +94,25 @@ namespace Umbraco.Web.Routing /// public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) { - var node = umbracoContext.ContentCache.GetById(id); + //get the invariant route for this item, this will give us the Id of it's domain node if one is assigned + var invariantRoute = umbracoContext.ContentCache.GetRouteById(id); + + if (string.IsNullOrWhiteSpace(invariantRoute)) + { + _logger.Debug(() => $"Couldn't find any page with nodeId={id}. This is most likely caused by the page not being published."); + return null; + } + var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); - var n = node; - var domainUris = domainHelper.DomainsForNode(n.Id, current, false); - while (domainUris == null && n != null) // n is null at root - { - // move to parent node - n = n.Parent; - domainUris = n == null ? null : domainHelper.DomainsForNode(n.Id, current); - } + // extract domainUri and path + // route is / or / + var pos = invariantRoute.IndexOf('/'); + var path = pos == 0 ? invariantRoute : invariantRoute.Substring(pos); + var domainUris = pos == 0 ? null : domainHelper.DomainsForNode(int.Parse(invariantRoute.Substring(0, pos)), current); - if (domainUris == null) - { - //there are no domains, exit + if (domainUris ==null) return Enumerable.Empty(); - } var result = new List(); foreach (var d in domainUris) @@ -120,8 +122,8 @@ namespace Umbraco.Web.Routing if (route == null) continue; //need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned) - var pos = route.IndexOf('/'); - var path = pos == 0 ? route : route.Substring(pos); + pos = route.IndexOf('/'); + path = pos == 0 ? route : route.Substring(pos); var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path)); uri = UriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings); diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index a71bb51ddc..f13e02381f 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.Routing if (contentService == null) throw new ArgumentNullException(nameof(contentService)); if (logger == null) throw new ArgumentNullException(nameof(logger)); - var urls = new List(); + var urls = new HashSet(); if (content.Published == false) { @@ -104,8 +104,11 @@ namespace Umbraco.Web.Routing } else { - urls.Add(url); - urls.AddRange(urlProvider.GetOtherUrls(content.Id)); + urls.Add(url); + foreach(var otherUrl in urlProvider.GetOtherUrls(content.Id)) + { + urls.Add(otherUrl); + } } } return urls; From 5b555d35d3bf33b5e642e714e46007f3820669f4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Apr 2018 15:35:11 +1000 Subject: [PATCH 50/97] adds test --- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 038211b86a..3b4dd5d069 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -153,6 +153,46 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(niceUrlMatch, result); } + [Test] + public void Get_Url_For_Culture_Variant_Without_Domains_Non_Current_Url() + { + const string currentUri = "http://example.us/test"; + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var publishedContentCache = new Mock(); + publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) + .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard + + var domainCache = new Mock(); + domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) + .Returns((int contentId, bool includeWildcards) => Enumerable.Empty()); + + var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); + + var snapshotService = new Mock(); + snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) + .Returns(snapshot); + + var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: _umbracoSettings, + urlProviders: new[] { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, + globalSettings: globalSettings.Object, + snapshotService: snapshotService.Object); + + //even though we are asking for a specific culture URL, there are no domains assigned so all that can be returned is a normal relative url. + var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); + + Assert.AreEqual("/home/test-fr/", url); + } + /// /// This tests DefaultUrlProvider.GetUrl with a specific culture when the current URL is the culture specific domain /// From dbad0d261e4b471e82da35949a6393d0b883e204 Mon Sep 17 00:00:00 2001 From: Simon Dingley Date: Fri, 27 Apr 2018 16:18:25 +0100 Subject: [PATCH 51/97] Fixes U4-8516 Copied node now sets the current user as the creator of the new node. --- 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 da5e0c3a3b..84acff37f8 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -565,7 +565,7 @@ namespace Umbraco.Web.Editors { var toCopy = ValidateMoveOrCopy(copy); - var c = Services.ContentService.Copy(toCopy, copy.ParentId, copy.RelateToOriginal, copy.Recursive); + var c = Services.ContentService.Copy(toCopy, copy.ParentId, copy.RelateToOriginal, copy.Recursive, Security.CurrentUser.Id); var response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StringContent(c.Path, Encoding.UTF8, "application/json"); From 9407f755221894ac662fc40657dcd687279ce0f6 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 27 Apr 2018 13:20:22 +0200 Subject: [PATCH 52/97] Fix importing test --- .../Importing/StandardMvc-Package.xml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml b/src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml index 9a776805e9..2b2c8da067 100644 --- a/src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml +++ b/src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml @@ -1099,7 +1099,7 @@ Google Maps - A map macro that you can use within Rich Text Areas Articles SW_Master - ClientAreas SW_Master - x.IsVisible() && x.TemplateId > 0 && Umbraco.MemberHasAccess(x.Id, x.Path)); @@ -1238,7 +1238,7 @@ Google Maps - A map macro that you can use within Rich Text Areas Contact SW_Master - Home SW_Master - Login SW_Master - Search SW_Master - Sitemap SW_Master - StandardPage SW_Master - SW_Master SW_Master - /usercontrols/StandardWebsiteInstall.ascx - \ No newline at end of file + From dbf1b1e0d4601d9994472d56e47595b766710c46 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 26 Apr 2018 16:03:08 +0200 Subject: [PATCH 53/97] Refactor DomainHelper --- src/Umbraco.Core/Models/Content.cs | 6 +- src/Umbraco.Core/Models/DomainExtensions.cs | 22 -- .../Repositories/ILanguageRepository.cs | 29 +++ .../Implement/LanguageRepository.cs | 34 +++ .../Services/ILocalizationService.cs | 31 ++- .../Services/Implement/LocalizationService.cs | 27 +++ .../Services/LocalizationServiceExtensions.cs | 36 --- src/Umbraco.Core/StringExtensions.cs | 6 + src/Umbraco.Core/Umbraco.Core.csproj | 2 - .../Routing/ContentFinderByUrlTests.cs | 4 +- .../Routing/SiteDomainHelperTests.cs | 111 +++++----- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 10 +- src/Umbraco.Web/Editors/ContentController.cs | 2 +- .../PublishedCache/IDomainCache.cs | 1 + .../PublishedCache/NuCache/DomainCache.cs | 5 +- .../NuCache/PublishedSnapshotService.cs | 22 +- .../XmlPublishedCache/DomainCache.cs | 10 +- src/Umbraco.Web/PublishedContentExtensions.cs | 98 ++++---- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 4 +- src/Umbraco.Web/Routing/Domain.cs | 9 +- src/Umbraco.Web/Routing/DomainHelper.cs | 209 +++++++++++------- src/Umbraco.Web/Routing/ISiteDomainHelper.cs | 22 +- src/Umbraco.Web/Routing/IUrlProvider.cs | 1 + src/Umbraco.Web/Routing/PublishedRouter.cs | 9 +- src/Umbraco.Web/Routing/SiteDomainHelper.cs | 118 ++++------ src/Umbraco.Web/Routing/UrlProvider.cs | 2 +- .../Trees/ContentTreeControllerBase.cs | 4 +- src/Umbraco.Web/UmbracoContext.cs | 34 ++- 28 files changed, 493 insertions(+), 375 deletions(-) delete mode 100644 src/Umbraco.Core/Models/DomainExtensions.cs delete mode 100644 src/Umbraco.Core/Services/LocalizationServiceExtensions.cs diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 6b8732e70c..8bfb5718a2 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -271,7 +271,7 @@ namespace Umbraco.Core.Models } /// - public IEnumerable PublishedCultures => _publishInfos?.Keys; + public IEnumerable PublishedCultures => _publishInfos?.Keys ?? Enumerable.Empty(); /// public bool IsCultureEdited(string culture) @@ -350,8 +350,8 @@ namespace Umbraco.Core.Models { var name = GetName(culture); if (string.IsNullOrWhiteSpace(name)) - return false; //fixme this should return an attempt with error results - + return false; //fixme this should return an attempt with error results + SetPublishInfos(culture, name, DateTime.Now); } diff --git a/src/Umbraco.Core/Models/DomainExtensions.cs b/src/Umbraco.Core/Models/DomainExtensions.cs deleted file mode 100644 index 616685b6b9..0000000000 --- a/src/Umbraco.Core/Models/DomainExtensions.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Umbraco.Core.Services; - -namespace Umbraco.Core.Models -{ - public static class DomainExtensions - { - /// - /// Returns ture if the has a culture code equal to the default language specified - /// - /// - /// - /// - public static bool IsDefaultDomain(this IDomain domain, ILocalizationService localizationService) - { - var defaultLang = localizationService.GetDefaultVariantLanguage(); - if (defaultLang == null) - return false; //if for some reason a null value is returned (i.e. no languages or based on mock unit test data), then assume false - - return domain.LanguageIsoCode == defaultLang.CultureName; - } - } -} diff --git a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs index 5831fcc7e4..b86898f97a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs @@ -6,7 +6,36 @@ namespace Umbraco.Core.Persistence.Repositories { ILanguage GetByIsoCode(string isoCode); + /// + /// Gets a language identifier from its ISO code. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// int? GetIdByIsoCode(string isoCode); + + /// + /// Gets a language ISO code from its identifier. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// string GetIsoCodeById(int? id); + + /// + /// Gets the default language ISO code. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + string GetDefaultIsoCode(); + + /// + /// Gets the default language identifier. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + int? GetDefaultId(); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 44a1ff6eeb..8e42ee460b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -242,5 +242,39 @@ namespace Umbraco.Core.Persistence.Repositories.Implement throw new ArgumentException($"Id {id} does not correspond to an existing language.", nameof(id)); return null; } + + public string GetDefaultIsoCode() + { + return GetDefault()?.IsoCode; + } + + public int? GetDefaultId() + { + return GetDefault()?.Id; + } + + // do NOT leak that language, it's not deep-cloned! + private ILanguage GetDefault() + { + // FIXME + var temp = SqlContext.Sql(); + if (temp == null) return null; + + // get all cached, non-cloned + var all = TypedCachePolicy.GetAllCached(PerformGetAll); + + ILanguage first = null; + foreach (var language in all) + { + // if one language is default, return + if (language.IsDefaultVariantLanguage) + return language; + // keep track of language with lowest id + if (first == null || language.Id < first.Id) + first = language; + } + + return first; + } } } diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs index d6eb3f24b4..aedf84f804 100644 --- a/src/Umbraco.Core/Services/ILocalizationService.cs +++ b/src/Umbraco.Core/Services/ILocalizationService.cs @@ -111,10 +111,37 @@ namespace Umbraco.Core.Services ILanguage GetLanguageByIsoCode(string isoCode); /// - /// Gets a language identifier by its iso code. + /// Gets a language identifier from its ISO code. /// - int? GetLanguageIdByIsoCode(string isoCode); + /// + /// This can be optimized and bypass all deep cloning. + /// + int? GetLanguageIdByIsoCode(string isoCode); + /// + /// Gets a language ISO code from its identifier. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + string GetLanguageIsoCodeById(int id); + + /// + /// Gets the default language ISO code. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + string GetDefaultLanguageIsoCode(); + + /// + /// Gets the default language identifier. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + int? GetDefaultLanguageId(); + /// /// Gets all available languages /// diff --git a/src/Umbraco.Core/Services/Implement/LocalizationService.cs b/src/Umbraco.Core/Services/Implement/LocalizationService.cs index 976c65e3e8..663ecf586c 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizationService.cs @@ -312,6 +312,33 @@ namespace Umbraco.Core.Services.Implement } } + /// + public string GetLanguageIsoCodeById(int id) + { + using (ScopeProvider.CreateScope(autoComplete: true)) + { + return _languageRepository.GetIsoCodeById(id); + } + } + + /// + public string GetDefaultLanguageIsoCode() + { + using (ScopeProvider.CreateScope(autoComplete: true)) + { + return _languageRepository.GetDefaultIsoCode(); + } + } + + /// + public int? GetDefaultLanguageId() + { + using (ScopeProvider.CreateScope(autoComplete: true)) + { + return _languageRepository.GetDefaultId(); + } + } + /// /// Gets all available languages /// diff --git a/src/Umbraco.Core/Services/LocalizationServiceExtensions.cs b/src/Umbraco.Core/Services/LocalizationServiceExtensions.cs deleted file mode 100644 index a597b64f3b..0000000000 --- a/src/Umbraco.Core/Services/LocalizationServiceExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Linq; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Services -{ - public static class LocalizationServiceExtensions - { - /// - /// Returns the configured default variant language - /// - /// - /// - public static ILanguage GetDefaultVariantLanguage(this ILocalizationService service) - { - var langs = service.GetAllLanguages().OrderBy(x => x.Id).ToList(); - - if (langs.Count == 0) return null; - - //if there's only one language, by default it is the default - if (langs.Count == 1) - { - langs[0].IsDefaultVariantLanguage = true; - langs[0].Mandatory = true; - return langs[0]; - } - - var foundDefault = langs.FirstOrDefault(x => x.IsDefaultVariantLanguage); - if (foundDefault != null) return foundDefault; - - //if no language has the default flag, then the defaul language is the one with the lowest id - langs[0].IsDefaultVariantLanguage = true; - langs[0].Mandatory = true; - return langs[0]; - } - } -} diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 0985e0521c..afdb4c3646 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -1487,5 +1487,11 @@ namespace Umbraco.Core guid[left] = guid[right]; guid[right] = temp; } + + /// + /// Turns an null-or-whitespace string into a null string. + /// + public static string NullEmpty(this string text) + => string.IsNullOrWhiteSpace(text) ? null : text; } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4d30318a15..2a4a79180b 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -351,7 +351,6 @@ - @@ -1420,7 +1419,6 @@ - diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs index 108abfe446..8b591dfb84 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs @@ -124,7 +124,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.Domain = new DomainAndUri(new Domain(1, "mysite", -1, CultureInfo.CurrentCulture, false, true), new Uri("http://mysite/")); + frequest.Domain = new DomainAndUri(new Domain(1, "mysite", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/")); var lookup = new ContentFinderByUrl(Logger); var result = lookup.TryFindContent(frequest); @@ -154,7 +154,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.Domain = new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false, true), new Uri("http://mysite/æøå")); + frequest.Domain = new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/æøå")); var lookup = new ContentFinderByUrl(Logger); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs b/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs index fea8948b8e..d9760f58c1 100644 --- a/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs +++ b/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs @@ -21,6 +21,9 @@ namespace Umbraco.Tests.Routing SiteDomainHelper.Clear(); // assuming this works! } + private static CultureInfo CultureFr = CultureInfo.GetCultureInfo("fr-fr"); + private static CultureInfo CultureUk = CultureInfo.GetCultureInfo("en-uk"); + [Test] public void AddSites() { @@ -188,38 +191,38 @@ namespace Umbraco.Tests.Routing var current = new Uri("https://www.domain1.com/foo/bar"); var domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, true), - new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false, false), + new Domain(1, "domain2.com", -1, CultureFr, false), + new Domain(1, "domain1.com", -1, CultureUk, false), }); - var output = helper.MapDomain(current, domainAndUris).Uri.ToString(); + var output = helper.MapDomain(domainAndUris, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); // will pick it all right current = new Uri("https://domain1.com/foo/bar"); domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false, true), - new Domain(1, "https://domain2.com", -1, CultureInfo.CurrentCulture, false, false) + new Domain(1, "https://domain1.com", -1, CultureFr, false), + new Domain(1, "https://domain2.com", -1, CultureUk, false) }); - output = helper.MapDomain(current, domainAndUris).Uri.ToString(); + output = helper.MapDomain(domainAndUris, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); current = new Uri("https://domain1.com/foo/bar"); domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false, true), - new Domain(1, "https://domain4.com", -1, CultureInfo.CurrentCulture, false, false) + new Domain(1, "https://domain1.com", -1, CultureFr, false), + new Domain(1, "https://domain4.com", -1, CultureUk, false) }); - output = helper.MapDomain(current, domainAndUris).Uri.ToString(); + output = helper.MapDomain(domainAndUris, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); current = new Uri("https://domain4.com/foo/bar"); domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false, true), - new Domain(1, "https://domain4.com", -1, CultureInfo.CurrentCulture, false, false) + new Domain(1, "https://domain1.com", -1, CultureFr, false), + new Domain(1, "https://domain4.com", -1, CultureUk, false) }); - output = helper.MapDomain(current, domainAndUris).Uri.ToString(); + output = helper.MapDomain(domainAndUris, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("https://domain4.com/", output); } @@ -241,22 +244,22 @@ namespace Umbraco.Tests.Routing // so we'll get current // var current = new Uri("http://domain1.com/foo/bar"); - var output = helper.MapDomain(current, new[] + var output = helper.MapDomain(new[] { - new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false, true), current), - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), - }).Uri.ToString(); + new DomainAndUri(new Domain(1, "domain1.com", -1, CultureFr, false), current), + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), + }, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("http://domain1.com/", output); // current is a site1 uri, domains do not contain current // so we'll get the corresponding site1 domain // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomain(current, new[] + output = helper.MapDomain(new[] { - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false, true), current), - new DomainAndUri(new Domain(1, "domain2.net", -1, CultureInfo.CurrentCulture, false, false), current) - }).Uri.ToString(); + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureFr, false), current), + new DomainAndUri(new Domain(1, "domain2.net", -1, CultureUk, false), current) + }, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("http://domain1.net/", output); // current is a site1 uri, domains do not contain current @@ -264,11 +267,11 @@ namespace Umbraco.Tests.Routing // order does not matter // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomain(current, new[] + output = helper.MapDomain(new[] { - new DomainAndUri(new Domain(1, "domain2.net", -1, CultureInfo.CurrentCulture, false, true), current), - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false, false), current) - }).Uri.ToString(); + new DomainAndUri(new Domain(1, "domain2.net", -1, CultureFr, false), current), + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureUk, false), current) + }, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("http://domain1.net/", output); } @@ -291,14 +294,14 @@ namespace Umbraco.Tests.Routing // current is a site1 uri, domains contains current // var current = new Uri("http://domain1.com/foo/bar"); - var output = helper.MapDomains(current, new[] + var output = helper.MapDomains(new[] { - new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false, true), current), // no: current + what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: same site (though bogus setup) - }, true).ToArray(); + new DomainAndUri(new Domain(1, "domain1.com", -1, CultureFr, false), current), // no: current + what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureUk, false), current), // yes: same site (though bogus setup) + }, current, true, CultureFr.Name, CultureFr.Name).ToArray(); Assert.AreEqual(1, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); @@ -306,14 +309,14 @@ namespace Umbraco.Tests.Routing // current is a site1 uri, domains does not contain current // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomains(current, new[] + output = helper.MapDomains(new[] { - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false, true), current), // no: what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: same site (though bogus setup) - }, true).ToArray(); + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureFr, false), current), // no: what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureUk, false), current), // yes: same site (though bogus setup) + }, current, true, CultureFr.Name, CultureFr.Name).ToArray(); Assert.AreEqual(1, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); @@ -324,15 +327,15 @@ namespace Umbraco.Tests.Routing // current is a site1 uri, domains contains current // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomains(current, new[] + output = helper.MapDomains(new[] { - new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false, true), current), // no: current + what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain3.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: same site (though bogus setup) - }, true).ToArray(); + new DomainAndUri(new Domain(1, "domain1.com", -1, CultureFr, false), current), // no: current + what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureUk, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain3.org", -1, CultureUk, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureUk, false), current), // yes: same site (though bogus setup) + }, current, true, CultureFr.Name, CultureFr.Name).ToArray(); Assert.AreEqual(3, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); @@ -342,15 +345,15 @@ namespace Umbraco.Tests.Routing // current is a site1 uri, domains does not contain current // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomains(current, new[] + output = helper.MapDomains(new[] { - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false, true), current), // no: what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain3.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false, false), current), // yes: same site (though bogus setup) - }, true).ToArray(); + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureFr, false), current), // no: what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureUk, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain3.org", -1, CultureUk, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureUk, false), current), // yes: same site (though bogus setup) + }, current, true, CultureFr.Name, CultureFr.Name).ToArray(); Assert.AreEqual(3, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 3b4dd5d069..185812002d 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -220,10 +220,11 @@ namespace Umbraco.Tests.Routing if (contentId != 9876) return Enumerable.Empty(); return new[] { - new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false, true), //default - new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false, true) + new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false), //default + new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false) }; }); + domainCache.Setup(x => x.DefaultCulture).Returns(CultureInfo.GetCultureInfo("en-US").Name); var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); @@ -271,10 +272,11 @@ namespace Umbraco.Tests.Routing if (contentId != 9876) return Enumerable.Empty(); return new[] { - new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false, true), //default - new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false, true) + new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false), //default + new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false) }; }); + domainCache.Setup(x => x.DefaultCulture).Returns(CultureInfo.GetCultureInfo("en-US").Name); var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index ffaffa05f4..f1528c7fe6 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1191,7 +1191,7 @@ namespace Umbraco.Web.Editors //sent up, then it means that the user is editing the default variant language. if (culture == null && content.HasPropertyTypeVaryingByCulture()) { - culture = Services.LocalizationService.GetDefaultVariantLanguage().IsoCode; + culture = Services.LocalizationService.GetDefaultLanguageIsoCode(); } var display = ContextMapper.Map(content, UmbracoContext, diff --git a/src/Umbraco.Web/PublishedCache/IDomainCache.cs b/src/Umbraco.Web/PublishedCache/IDomainCache.cs index 113533f500..bace337862 100644 --- a/src/Umbraco.Web/PublishedCache/IDomainCache.cs +++ b/src/Umbraco.Web/PublishedCache/IDomainCache.cs @@ -7,5 +7,6 @@ namespace Umbraco.Web.PublishedCache { IEnumerable GetAll(bool includeWildcards); IEnumerable GetAssigned(int contentId, bool includeWildcards); + string DefaultCulture { get; } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs index 3b2165c00a..896a04a0b3 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs @@ -8,9 +8,10 @@ namespace Umbraco.Web.PublishedCache.NuCache { private readonly SnapDictionary.Snapshot _snapshot; - public DomainCache(SnapDictionary.Snapshot snapshot) + public DomainCache(SnapDictionary.Snapshot snapshot, string defaultCulture) { _snapshot = snapshot; + DefaultCulture = defaultCulture; // capture - fast } public IEnumerable GetAll(bool includeWildcards) @@ -30,5 +31,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (includeWildcards == false) list = list.Where(x => x.IsWildcard == false); return list; } + + public string DefaultCulture { get; } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index e611a738e5..923502bea2 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -487,7 +487,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var domains = _serviceContext.DomainService.GetAll(true); foreach (var domain in domains .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard, x.IsDefaultDomain(_serviceContext.LocalizationService)))) + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard))) { _domainStore.Set(domain.Id, domain); } @@ -831,7 +831,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (domain.RootContentId.HasValue == false) continue; // anomaly if (domain.LanguageIsoCode.IsNullOrWhiteSpace()) continue; // anomaly var culture = CultureInfo.GetCultureInfo(domain.LanguageIsoCode); - _domainStore.Set(domain.Id, new Domain(domain.Id, domain.DomainName, domain.RootContentId.Value, culture, domain.IsWildcard, domain.IsDefaultDomain(_serviceContext.LocalizationService))); + _domainStore.Set(domain.Id, new Domain(domain.Id, domain.DomainName, domain.RootContentId.Value, culture, domain.IsWildcard)); break; } } @@ -1014,7 +1014,8 @@ namespace Umbraco.Web.PublishedCache.NuCache var memberTypeCache = new PublishedContentTypeCache(null, null, _serviceContext.MemberTypeService, _publishedContentTypeFactory, _logger); - var domainCache = new DomainCache(domainSnap); + var defaultCulture = _serviceContext.LocalizationService.GetDefaultLanguageIsoCode(); // capture - fast + var domainCache = new DomainCache(domainSnap, defaultCulture); var domainHelper = new DomainHelper(domainCache, _siteDomainHelper); return new PublishedSnapshot.PublishedSnapshotElements @@ -1214,12 +1215,17 @@ namespace Umbraco.Web.PublishedCache.NuCache } var cultureData = new Dictionary(); - if (content.Names != null) + + // fixme refactor!!! + var names = content is IContent document + ? (published + ? document.PublishNames + : document.Names) + : content.Names; + + foreach (var (culture, name) in names) { - foreach (var name in content.Names) - { - cultureData[name.Key] = new CultureVariation { Name = name.Value }; - } + cultureData[culture] = new CultureVariation { Name = name }; } //the dictionary that will be serialized diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs index 051c333762..64c1d80852 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs @@ -4,33 +4,33 @@ using System.Linq; using Umbraco.Web.Routing; using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Core.Models; namespace Umbraco.Web.PublishedCache.XmlPublishedCache { internal class DomainCache : IDomainCache { private readonly IDomainService _domainService; - private readonly ILocalizationService _localizationService; public DomainCache(IDomainService domainService, ILocalizationService localizationService) { _domainService = domainService; - _localizationService = localizationService; + DefaultCulture = localizationService.GetDefaultLanguageIsoCode(); // capture - fast } public IEnumerable GetAll(bool includeWildcards) { return _domainService.GetAll(includeWildcards) .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard, x.IsDefaultDomain(_localizationService))); + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard)); } public IEnumerable GetAssigned(int contentId, bool includeWildcards) { return _domainService.GetAssignedDomains(contentId, includeWildcards) .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard, x.IsDefaultDomain(_localizationService))); + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard)); } + + public string DefaultCulture { get; } } } diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index d11a168b88..ace05c33f5 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -222,8 +222,8 @@ namespace Umbraco.Web #region Search public static IEnumerable Search(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) - { - //TODO: we should pass in the IExamineManager? + { + //TODO: we should pass in the IExamineManager? var searcher = string.IsNullOrEmpty(indexName) ? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) @@ -249,7 +249,7 @@ namespace Umbraco.Web public static IEnumerable SearchChildren(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) { - //TODO: we should pass in the IExamineManager? + //TODO: we should pass in the IExamineManager? var searcher = string.IsNullOrEmpty(indexName) ? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) @@ -270,10 +270,10 @@ namespace Umbraco.Web public static IEnumerable Search(this IPublishedContent content, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searchProvider = null) { - //TODO: we should pass in the IExamineManager? + //TODO: we should pass in the IExamineManager? + + var s = searchProvider ?? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); - var s = searchProvider ?? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); - var results = s.Search(criteria); return results.ToPublishedSearchResults(UmbracoContext.Current.ContentCache); } @@ -1116,12 +1116,12 @@ namespace Umbraco.Web return new DataTable(); //no children found //use new utility class to create table so that we don't have to maintain code in many places, just one - var dt = Core.DataTableExtensions.GenerateDataTable( - //pass in the alias of the first child node since this is the node type we're rendering headers for - firstNode.DocumentTypeAlias, - //pass in the callback to extract the Dictionary of all defined aliases to their names - alias => GetPropertyAliasesAndNames(services, alias), - //pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place. + var dt = Core.DataTableExtensions.GenerateDataTable( + //pass in the alias of the first child node since this is the node type we're rendering headers for + firstNode.DocumentTypeAlias, + //pass in the callback to extract the Dictionary of all defined aliases to their names + alias => GetPropertyAliasesAndNames(services, alias), + //pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place. () => { //create all row data @@ -1222,51 +1222,51 @@ namespace Umbraco.Web private static Dictionary GetAliasesAndNames(IContentTypeBase contentType) { return contentType.PropertyTypes.ToDictionary(x => x.Alias, x => x.Name); - } - + } + #endregion - + #region Culture - - //TODO: Not used - ///// - ///// Gets the culture that would be selected to render a specified content, - ///// within the context of a specified current request. - ///// - ///// The content. - ///// The request Uri. - ///// The culture that would be selected to render the content. - //public static CultureInfo GetCulture(this IPublishedContent content, Uri current = null) - //{ - // return Models.ContentExtensions.GetCulture(UmbracoContext.Current, - // Current.Services.DomainService, - // Current.Services.LocalizationService, - // Current.Services.ContentService, - // content.Id, content.Path, - // current); - //} - + + //TODO: Not used + ///// + ///// Gets the culture that would be selected to render a specified content, + ///// within the context of a specified current request. + ///// + ///// The content. + ///// The request Uri. + ///// The culture that would be selected to render the content. + //public static CultureInfo GetCulture(this IPublishedContent content, Uri current = null) + //{ + // return Models.ContentExtensions.GetCulture(UmbracoContext.Current, + // Current.Services.DomainService, + // Current.Services.LocalizationService, + // Current.Services.ContentService, + // content.Id, content.Path, + // current); + //} + /// /// Return the URL name for the based on the culture specified or default culture defined /// /// /// /// - /// - public static string GetUrlName(this IPublishedContent content, ILocalizationService localizationService, string culture = null) - { - if (content.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) - { - var cultureCode = culture ?? localizationService.GetDefaultVariantLanguage()?.IsoCode; - if (cultureCode != null && content.CultureNames.TryGetValue(cultureCode, out var cultureName)) - { - return cultureName.UrlName; - } - } - - //if we get here, the content type is invariant or we don't have access to a usable culture code - return content.UrlName; - } + /// + public static string GetUrlName(this IPublishedContent content, ILocalizationService localizationService, string culture = null) + { + if (content.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) + { + var cultureCode = culture ?? localizationService.GetDefaultLanguageIsoCode(); + if (cultureCode != null && content.CultureNames.TryGetValue(cultureCode, out var cultureName)) + { + return cultureName.UrlName; + } + } + + //if we get here, the content type is invariant or we don't have access to a usable culture code + return content.UrlName; + } #endregion } diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index e09f343555..37d38d521e 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -38,6 +38,7 @@ namespace Umbraco.Web.Routing /// The published content id. /// The current absolute url. /// The url mode. + /// The culture. /// The url for the published content. /// /// The url is absolute or relative depending on mode and on current. @@ -45,8 +46,7 @@ namespace Umbraco.Web.Routing /// public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null) { - if (!current.IsAbsoluteUri) - throw new ArgumentException("Current url must be absolute.", "current"); + if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", nameof(current)); // will not use cache if previewing var route = umbracoContext.ContentCache.GetRouteById(id, culture); diff --git a/src/Umbraco.Web/Routing/Domain.cs b/src/Umbraco.Web/Routing/Domain.cs index 03048f0dd5..b9116c6b51 100644 --- a/src/Umbraco.Web/Routing/Domain.cs +++ b/src/Umbraco.Web/Routing/Domain.cs @@ -15,14 +15,13 @@ namespace Umbraco.Web.Routing /// The identifier of the content which supports the domain. /// The culture of the domain. /// A value indicating whether the domain is a wildcard domain. - public Domain(int id, string name, int contentId, CultureInfo culture, bool isWildcard, bool isDefault) + public Domain(int id, string name, int contentId, CultureInfo culture, bool isWildcard) { Id = id; Name = name; ContentId = contentId; Culture = culture; IsWildcard = isWildcard; - IsDefault = isDefault; } /// @@ -36,7 +35,6 @@ namespace Umbraco.Web.Routing ContentId = domain.ContentId; Culture = domain.Culture; IsWildcard = domain.IsWildcard; - IsDefault = domain.IsDefault; } /// @@ -63,10 +61,5 @@ namespace Umbraco.Web.Routing /// Gets a value indicating whether the domain is a wildcard domain. /// public bool IsWildcard { get; } - - /// - /// Gets a value indicating if this is the default domain for the website. - /// - public bool IsDefault { get; } } } diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index 76a0845113..3d6d435179 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; -using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; // published snapshot namespace Umbraco.Web.Routing @@ -12,13 +11,13 @@ namespace Umbraco.Web.Routing /// public class DomainHelper { - private readonly IDomainCache _domainCache; - private readonly ISiteDomainHelper _siteDomainHelper; - + private readonly IDomainCache _domainCache; + private readonly ISiteDomainHelper _siteDomainHelper; + public DomainHelper(IDomainCache domainCache, ISiteDomainHelper siteDomainHelper) { - _domainCache = domainCache; - _siteDomainHelper = siteDomainHelper; + _domainCache = domainCache; + _siteDomainHelper = siteDomainHelper; } #region Domain for Node @@ -28,9 +27,14 @@ namespace Umbraco.Web.Routing /// /// The node identifier. /// The uri, or null. - /// The domain and its uri, if any, that best matches the specified uri, else null. - /// If at least a domain is set on the node then the method returns the domain that - /// best matches the specified uri, else it returns null. + /// The culture, or null. + /// The domain and its uri, if any, that best matches the specified uri and culture, else null. + /// + /// f at least a domain is set on the node then the method returns the domain that + /// best matches the specified uri and culture, else it returns null. + /// If culture is null, uses the default culture for the installation instead. + /// fixme not exactly - if culture is !null, we MUST have a value for THAT culture, else we use default as hint + /// internal DomainAndUri DomainForNode(int nodeId, Uri current, string culture = null) { // be safe @@ -44,8 +48,9 @@ namespace Umbraco.Web.Routing if (domains.Length == 0) return null; - // else filter - var domainAndUri = DomainForUri(domains, current, culture, domainAndUris => _siteDomainHelper.MapDomain(current, domainAndUris)); + // else filter + var domainAndUri = SelectDomain(domains, current, culture, _domainCache.DefaultCulture, + (cdomainAndUris, ccurrent, cculture, cdefaultCulture) => _siteDomainHelper.MapDomain(cdomainAndUris, ccurrent, cculture, cdefaultCulture)); if (domainAndUri == null) throw new Exception("DomainForUri returned null."); @@ -86,107 +91,163 @@ namespace Umbraco.Web.Routing return null; // get the domains and their uris - var domainAndUris = DomainsForUri(domains, current).ToArray(); + var domainAndUris = SelectDomains(domains, current).ToArray(); // filter - return _siteDomainHelper.MapDomains(current, domainAndUris, excludeDefault).ToArray(); + return _siteDomainHelper.MapDomains(domainAndUris, current, excludeDefault, null, _domainCache.DefaultCulture).ToArray(); } #endregion - #region Domain for Uri + #region Selects Domain(s) /// - /// Finds the domain that best matches a specified uri, into a group of domains. + /// Selects the domain that best matches a specified uri and cultures, from a set of domains. /// /// The group of domains. - /// The uri, or null. - /// A function to filter the list of domains, if more than one applies, or null. - /// The domain and its normalized uri, that best matches the specified uri. + /// An optional uri. + /// An optional culture. + /// An optional default culture. + /// An optional function to filter the list of domains, if more than one applies. + /// The domain and its normalized uri, that best matches the specified uri and cultures. /// + /// fixme - must document and explain this all + /// If is null, pick the first domain that matches , + /// else the first that matches , else the first one (ordered by id), else null. + /// If is not null, look for domains that would be a base uri of the current uri, /// If more than one domain matches, then the function is used to pick /// the right one, unless it is null, in which case the method returns null. /// The filter, if any, will be called only with a non-empty argument, and _must_ return something. /// - internal static DomainAndUri DomainForUri(IEnumerable domains, Uri current, string culture = null, Func filter = null) + internal static DomainAndUri SelectDomain(IEnumerable domains, Uri uri, string culture = null, string defaultCulture = null, Func, Uri, string, string, DomainAndUri> filter = null) { // sanitize the list to have proper uris for comparison (scheme, path end with /) // we need to end with / because example.com/foo cannot match example.com/foobar // we need to order so example.com/foo matches before example.com/ var domainsAndUris = domains .Where(d => d.IsWildcard == false) - .Select(d => new DomainAndUri(d, current)) + .Select(d => new DomainAndUri(d, uri)) .OrderByDescending(d => d.Uri.ToString()) - .ToArray(); + .ToList(); - if (domainsAndUris.Length == 0) + // nothing = no magic, return null + if (domainsAndUris.Count == 0) return null; - DomainAndUri domainAndUri; - if (current == null) - { - //match either by culture (if specified) or default - - //get the default domain or the one matching the culture if specified - domainAndUri = domainsAndUris.FirstOrDefault(x => culture.IsNullOrWhiteSpace() ? x.IsDefault : x.Culture.Name.InvariantEquals(culture)); - - if (domainAndUri == null && !culture.IsNullOrWhiteSpace()) - throw new InvalidOperationException($"No domain was found by the specified culture '{culture}'"); - - if (domainAndUri == null) - domainAndUri = domainsAndUris.First(); // take the first one by default (what else can we do?) - } - else if (!culture.IsNullOrWhiteSpace()) - { - //match by culture - - domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture)); - if (domainAndUri == null) - throw new InvalidOperationException($"No domain was found by the specified culture '{culture}'"); - return domainAndUri; + // sanitize cultures + culture = culture.NullEmpty(); + defaultCulture = defaultCulture.NullEmpty(); + + if (uri == null) + { + // no uri - will only rely on culture + return GetByCulture(domainsAndUris, culture, defaultCulture); } - else - { - //try to match by current, else filter - // look for the first domain that would be the base of the current url - // ie current is www.example.com/foo/bar, look for domain www.example.com - var currentWithSlash = current.EndPathWithSlash(); - domainAndUri = domainsAndUris - .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash)); - if (domainAndUri != null) return domainAndUri; + // else we have a uri, + // try to match that uri, else filter - // if none matches, try again without the port - // ie current is www.example.com:1234/foo/bar, look for domain www.example.com - domainAndUri = domainsAndUris - .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash.WithoutPort())); + // pick domains for cultures + var cultureDomains = SelectByCulture(domainsAndUris, culture, defaultCulture); + IReadOnlyCollection considerForBaseDomains = domainsAndUris; + if (cultureDomains != null) + { + if (cultureDomains.Count == 1) // only 1, return + return cultureDomains.First(); + + // else restrict to those domains, for base lookup + considerForBaseDomains = cultureDomains; + } + + // look for domains that would be the base of the uri + var baseDomains = SelectByBase(considerForBaseDomains, uri); + if (baseDomains.Count > 0) // found, return + return baseDomains.First(); + + // if nothing works, then try to run the filter to select a domain + // either restricting on cultureDomains, or on all domains + if (filter != null) + { + var domainAndUri = filter(cultureDomains ?? domainsAndUris, uri, culture, defaultCulture); + // if still nothing, pick the first one? + // no: move that constraint to the filter, but check + if (domainAndUri == null) + throw new InvalidOperationException("The filter returned null."); + return domainAndUri; + } + + return null; + } + + private static IReadOnlyCollection SelectByBase(IReadOnlyCollection domainsAndUris, Uri uri) + { + // look for domains that would be the base of the uri + // ie current is www.example.com/foo/bar, look for domain www.example.com + var currentWithSlash = uri.EndPathWithSlash(); + var baseDomains = domainsAndUris.Where(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash)).ToList(); + + // if none matches, try again without the port + // ie current is www.example.com:1234/foo/bar, look for domain www.example.com + if (baseDomains.Count == 0) + baseDomains = domainsAndUris.Where(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash.WithoutPort())).ToList(); + + return baseDomains; + } + + private static IReadOnlyCollection SelectByCulture(IReadOnlyCollection domainsAndUris, string culture, string defaultCulture) + { + if (culture != null) // try the supplied culture + { + var cultureDomains = domainsAndUris.Where(x => x.Culture.Name.InvariantEquals(culture)).ToList(); + if (cultureDomains.Count > 0) return cultureDomains; + + // if a culture is supplied, we *want* a url for that culture, else fail + throw new InvalidOperationException($"Could not find a domain for culture \"{culture}\"."); + } + + if (defaultCulture != null) // try the defaultCulture culture + { + var cultureDomains = domainsAndUris.Where(x => x.Culture.Name.InvariantEquals(defaultCulture)).ToList(); + if (cultureDomains.Count > 0) return cultureDomains; + } + + return null; + } + + private static DomainAndUri GetByCulture(IReadOnlyCollection domainsAndUris, string culture, string defaultCulture) + { + DomainAndUri domainAndUri; + + if (culture != null) // try the supplied culture + { + domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture)); if (domainAndUri != null) return domainAndUri; - - // if none matches, then try to run the filter to pick a domain - if (filter != null) - { - domainAndUri = filter(domainsAndUris); - // if still nothing, pick the first one? - // no: move that constraint to the filter, but check - if (domainAndUri == null) - throw new InvalidOperationException("The filter returned null."); - } + + // if a culture is supplied, we *want* a url for that culture, else fail + throw new InvalidOperationException($"Could not find a domain for culture \"{culture}\"."); } - return domainAndUri; + if (defaultCulture != null) // try the defaultCulture culture + { + domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(defaultCulture)); + if (domainAndUri != null) return domainAndUri; + } + + return domainsAndUris.First(); // what else? } /// - /// Gets the domains that match a specified uri, into a group of domains. + /// Selects the domains that match a specified uri, from a set of domains. /// - /// The group of domains. - /// The uri, or null. + /// The domains. + /// The uri, or null. /// The domains and their normalized uris, that match the specified uri. - internal static IEnumerable DomainsForUri(IEnumerable domains, Uri current) - { + internal static IEnumerable SelectDomains(IEnumerable domains, Uri uri) + { + // fixme where are we matching ?!!? return domains .Where(d => d.IsWildcard == false) - .Select(d => new DomainAndUri(d, current)) + .Select(d => new DomainAndUri(d, uri)) .OrderByDescending(d => d.Uri.ToString()); } diff --git a/src/Umbraco.Web/Routing/ISiteDomainHelper.cs b/src/Umbraco.Web/Routing/ISiteDomainHelper.cs index 482cbba4a9..2ca8273f45 100644 --- a/src/Umbraco.Web/Routing/ISiteDomainHelper.cs +++ b/src/Umbraco.Web/Routing/ISiteDomainHelper.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; namespace Umbraco.Web.Routing { @@ -13,25 +11,35 @@ namespace Umbraco.Web.Routing /// /// Filters a list of DomainAndUri to pick one that best matches the current request. /// - /// The Uri of the current request. /// The list of DomainAndUri to filter. + /// The Uri of the current request. + /// A culture. + /// The default culture. /// The selected DomainAndUri. /// /// If the filter is invoked then is _not_ empty and /// is _not_ null, and could not be /// matched with anything in . + /// The may be null, but when non-null, it can be used + /// to help pick the best matches. /// The filter _must_ return something else an exception will be thrown. /// - DomainAndUri MapDomain(Uri current, DomainAndUri[] domainAndUris); + DomainAndUri MapDomain(IReadOnlyCollection domainAndUris, Uri current, string culture, string defaultCulture); /// /// Filters a list of DomainAndUri to pick those that best matches the current request. /// - /// The Uri of the current request. /// The list of DomainAndUri to filter. + /// The Uri of the current request. /// A value indicating whether to exclude the current/default domain. + /// A culture. + /// The default culture. /// The selected DomainAndUri items. - /// The filter must return something, even empty, else an exception will be thrown. - IEnumerable MapDomains(Uri current, DomainAndUri[] domainAndUris, bool excludeDefault); + /// + /// The filter must return something, even empty, else an exception will be thrown. + /// The may be null, but when non-null, it can be used + /// to help pick the best matches. + /// + IEnumerable MapDomains(IReadOnlyCollection domainAndUris, Uri current, bool excludeDefault, string culture, string defaultCulture); } } diff --git a/src/Umbraco.Web/Routing/IUrlProvider.cs b/src/Umbraco.Web/Routing/IUrlProvider.cs index e4c6e23022..031f4670b2 100644 --- a/src/Umbraco.Web/Routing/IUrlProvider.cs +++ b/src/Umbraco.Web/Routing/IUrlProvider.cs @@ -23,6 +23,7 @@ namespace Umbraco.Web.Routing /// If the provider is unable to provide a url, it should return null. /// string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null); + // FIXME WE HAVE TO DOCUMENT CULTURE FFS /// /// Gets the other urls of a published content. diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 0b4fc89b6b..35e5ab1af4 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -265,8 +265,12 @@ namespace Umbraco.Web.Routing _logger.Debug(() => $"{tracePrefix}Uri=\"{request.Uri}\""); + var domainsCache = request.UmbracoContext.PublishedSnapshot.Domains; + var domains = domainsCache.GetAll(includeWildcards: false); + var defaultCulture = domainsCache.DefaultCulture; + // try to find a domain matching the current request - var domainAndUri = DomainHelper.DomainForUri(request.UmbracoContext.PublishedSnapshot.Domains.GetAll(false), request.Uri); + var domainAndUri = DomainHelper.SelectDomain(domains, request.Uri, defaultCulture: defaultCulture); // handle domain - always has a contentId and a culture if (domainAndUri != null) @@ -289,8 +293,7 @@ namespace Umbraco.Web.Routing // not matching any existing domain _logger.Debug(() => $"{tracePrefix}Matches no domain"); - var defaultLanguage = _services.LocalizationService.GetAllLanguages().FirstOrDefault(); - request.Culture = defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode); + request.Culture = defaultCulture == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultCulture); } _logger.Debug(() => $"{tracePrefix}Culture=\"{request.Culture.Name}\""); diff --git a/src/Umbraco.Web/Routing/SiteDomainHelper.cs b/src/Umbraco.Web/Routing/SiteDomainHelper.cs index deaa84b0e1..6e592c471d 100644 --- a/src/Umbraco.Web/Routing/SiteDomainHelper.cs +++ b/src/Umbraco.Web/Routing/SiteDomainHelper.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Routing /// Provides utilities to handle site domains. /// public class SiteDomainHelper : ISiteDomainHelper - { + { #region Configure private static readonly ReaderWriterLockSlim ConfigLock = new ReaderWriterLockSlim(); @@ -20,31 +20,27 @@ namespace Umbraco.Web.Routing private static Dictionary> _qualifiedSites; // these are for unit tests *only* - internal static Dictionary Sites { get { return _sites; } } - internal static Dictionary> Bindings { get { return _bindings; } } + // ReSharper disable ConvertToAutoPropertyWithPrivateSetter + internal static Dictionary Sites => _sites; + internal static Dictionary> Bindings => _bindings; + // ReSharper restore ConvertToAutoPropertyWithPrivateSetter // these are for validation //private const string DomainValidationSource = @"^(\*|((?i:http[s]?://)?([-\w]+(\.[-\w]+)*)(:\d+)?(/[-\w]*)?))$"; private const string DomainValidationSource = @"^(((?i:http[s]?://)?([-\w]+(\.[-\w]+)*)(:\d+)?(/)?))$"; private static readonly Regex DomainValidation = new Regex(DomainValidationSource, RegexOptions.IgnoreCase | RegexOptions.Compiled); - /// + /// /// Returns a disposable object that represents safe write access to config. - /// + /// /// Should be used in a using(SiteDomainHelper.ConfigWriteLock) { ... } mode. - protected static IDisposable ConfigWriteLock - { - get { return new WriteLock(ConfigLock); } - } + protected static IDisposable ConfigWriteLock => new WriteLock(ConfigLock); /// /// Returns a disposable object that represents safe read access to config. /// /// Should be used in a using(SiteDomainHelper.ConfigWriteLock) { ... } mode. - protected static IDisposable ConfigReadLock - { - get { return new ReadLock(ConfigLock); } - } + protected static IDisposable ConfigReadLock => new ReadLock(ConfigLock); /// /// Clears the entire configuration. @@ -66,7 +62,7 @@ namespace Umbraco.Web.Routing return domains.Select(domain => { if (!DomainValidation.IsMatch(domain)) - throw new ArgumentOutOfRangeException("domains", string.Format("Invalid domain: \"{0}\"", domain)); + throw new ArgumentOutOfRangeException(nameof(domains), $"Invalid domain: \"{domain}\"."); return domain; }); } @@ -111,27 +107,27 @@ namespace Umbraco.Web.Routing { using (ConfigWriteLock) { - if (_sites != null && _sites.ContainsKey(key)) + if (_sites == null || !_sites.ContainsKey(key)) + return; + + _sites.Remove(key); + if (_sites.Count == 0) + _sites = null; + + if (_bindings != null && _bindings.ContainsKey(key)) { - _sites.Remove(key); - if (_sites.Count == 0) - _sites = null; - - if (_bindings != null && _bindings.ContainsKey(key)) + foreach (var b in _bindings[key]) { - foreach (var b in _bindings[key]) - { - _bindings[b].Remove(key); - if (_bindings[b].Count == 0) - _bindings.Remove(b); - } - _bindings.Remove(key); - if (_bindings.Count > 0) - _bindings = null; + _bindings[b].Remove(key); + if (_bindings[b].Count == 0) + _bindings.Remove(b); } - - _qualifiedSites = null; + _bindings.Remove(key); + if (_bindings.Count > 0) + _bindings = null; } + + _qualifiedSites = null; } } @@ -148,7 +144,7 @@ namespace Umbraco.Web.Routing using (ConfigWriteLock) { foreach (var key in keys.Where(key => !_sites.ContainsKey(key))) - throw new ArgumentException(string.Format("Not an existing site key: {0}", key), "keys"); + throw new ArgumentException($"Not an existing site key: {key}.", nameof(keys)); _bindings = _bindings ?? new Dictionary>(); @@ -173,36 +169,20 @@ namespace Umbraco.Web.Routing #region Map domains - /// - /// Filters a list of DomainAndUri to pick one that best matches the current request. - /// - /// The Uri of the current request. - /// The list of DomainAndUri to filter. - /// The selected DomainAndUri. - /// - /// If the filter is invoked then is _not_ empty and - /// is _not_ null, and could not be - /// matched with anything in . - /// The filter _must_ return something else an exception will be thrown. - /// - public virtual DomainAndUri MapDomain(Uri current, DomainAndUri[] domainAndUris) + /// + public virtual DomainAndUri MapDomain(IReadOnlyCollection domainAndUris, Uri current, string culture, string defaultCulture) { var currentAuthority = current.GetLeftPart(UriPartial.Authority); var qualifiedSites = GetQualifiedSites(current); - return MapDomain(domainAndUris, qualifiedSites, currentAuthority); + return MapDomain(domainAndUris, qualifiedSites, currentAuthority, culture, defaultCulture); } - /// - /// Filters a list of DomainAndUri to pick those that best matches the current request. - /// - /// The Uri of the current request. - /// The list of DomainAndUri to filter. - /// A value indicating whether to exclude the current/default domain. - /// The selected DomainAndUri items. - /// The filter must return something, even empty, else an exception will be thrown. - public virtual IEnumerable MapDomains(Uri current, DomainAndUri[] domainAndUris, bool excludeDefault) - { + /// + public virtual IEnumerable MapDomains(IReadOnlyCollection domainAndUris, Uri current, bool excludeDefault, string culture, string defaultCulture) + { + // fixme ignoring cultures entirely? + var currentAuthority = current.GetLeftPart(UriPartial.Authority); KeyValuePair[] candidateSites = null; IEnumerable ret = domainAndUris; @@ -225,7 +205,7 @@ namespace Umbraco.Web.Routing { // it is illegal to call MapDomain if domainAndUris is empty // also, domainAndUris should NOT contain current, hence the test on hinted - var mainDomain = MapDomain(domainAndUris, qualifiedSites, currentAuthority); // what GetUrl would get + var mainDomain = MapDomain(domainAndUris, qualifiedSites, currentAuthority, culture, defaultCulture); // what GetUrl would get ret = ret.Where(d => d != mainDomain); } } @@ -259,7 +239,7 @@ namespace Umbraco.Web.Routing var authority = d.Uri.GetLeftPart(UriPartial.Authority); return candidateSites.Any(site => site.Value.Contains(authority)); }); - } + } private static Dictionary GetQualifiedSites(Uri current) { @@ -294,21 +274,19 @@ namespace Umbraco.Web.Routing // therefore it is safe to return and exit the configuration lock } - private static DomainAndUri MapDomain(DomainAndUri[] domainAndUris, Dictionary qualifiedSites, string currentAuthority) + private static DomainAndUri MapDomain(IReadOnlyCollection domainAndUris, Dictionary qualifiedSites, string currentAuthority, string culture, string defaultCulture) { - if (domainAndUris == null) - throw new ArgumentNullException("domainAndUris"); - if (!domainAndUris.Any()) - throw new ArgumentException("Cannot be empty.", "domainAndUris"); + if (domainAndUris == null) throw new ArgumentNullException(nameof(domainAndUris)); + if (domainAndUris.Count == 0) throw new ArgumentException("Cannot be empty.", nameof(domainAndUris)); + + // fixme how shall we deal with cultures? // we do our best, but can't do the impossible + // get the "default" domain ie the first one for the culture, else the first one (exists, length > 0) if (qualifiedSites == null) - { - //fixme take the default - var defaultDomain = domainAndUris.FirstOrDefault(x => x.IsDefault); - if (defaultDomain == null) defaultDomain = domainAndUris.First(); //this shouldn't occur but just in case - return defaultDomain; - } + return domainAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture)) ?? + domainAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(defaultCulture)) ?? + domainAndUris.First(); // find a site that contains the current authority var currentSite = qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority)); @@ -330,7 +308,7 @@ namespace Umbraco.Web.Routing .FirstOrDefault(domainAndUri => domainAndUri != null); // random, really - ret = ret ?? domainAndUris.First(); + ret = ret ?? domainAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture)) ?? domainAndUris.First(); return ret; } diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 98bd1b182d..c23ff5fb86 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -209,7 +209,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, Uri current, UrlProviderMode mode, string culture = null) + public string GetUrl(int id, Uri current, UrlProviderMode mode, string culture = null) // FIXME DOCUMENT { var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, id, current, mode, culture)) .FirstOrDefault(u => u != null); diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 96a1029f3c..4e7b78efe9 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -210,8 +210,8 @@ namespace Umbraco.Web.Trees result = Services.EntityService.GetChildren(entityId, UmbracoObjectType).ToArray(); } - //This should really never be null, but we'll error check anyways - int? currLangId = langId.HasValue ? langId.Value : Services.LocalizationService.GetDefaultVariantLanguage()?.Id; + //This should really never be null, but we'll error check anyways + var currLangId = langId ?? Services.LocalizationService.GetDefaultLanguageId(); //Try to see if there is a variant name for the current language for the item and set the name accordingly. //If any of this fails, the tree node name will remain the default invariant culture name. diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index c58ff40575..b86c8c29a6 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -39,16 +39,16 @@ namespace Umbraco.Web /// The "current" UmbracoContext. /// /// fixme - this needs to be clarified - /// + /// /// If is true then the "current" UmbracoContext is replaced /// with a new one even if there is one already. See . Has to do with /// creating a context at startup and not being able to access httpContext.Request at that time, so /// the OriginalRequestUrl remains unspecified until replaces the context. - /// + /// /// This *has* to be done differently! - /// + /// /// See http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 - /// + /// /// // used by // UmbracoModule BeginRequest (since it's a request it has an UmbracoContext) @@ -215,21 +215,17 @@ namespace Umbraco.Web /// /// Creates and caches an instance of a DomainHelper - /// - /// - /// We keep creating new instances of DomainHelper, it would be better if we didn't have to do that so instead we can - /// have one attached to the UmbracoContext. This method accepts an external ISiteDomainHelper otherwise the UmbracoContext - /// ctor will have to have another parameter added only for this one method which is annoying and doesn't make a ton of sense - /// since the UmbracoContext itself doesn't use this. - /// - /// TODO The alternative is to have a IDomainHelperAccessor singleton which is cached per UmbracoContext - /// - internal DomainHelper GetDomainHelper(ISiteDomainHelper siteDomainHelper) - { - if (_domainHelper == null) - _domainHelper = new DomainHelper(PublishedSnapshot.Domains, siteDomainHelper); - return _domainHelper; - } + /// + /// + /// We keep creating new instances of DomainHelper, it would be better if we didn't have to do that so instead we can + /// have one attached to the UmbracoContext. This method accepts an external ISiteDomainHelper otherwise the UmbracoContext + /// ctor will have to have another parameter added only for this one method which is annoying and doesn't make a ton of sense + /// since the UmbracoContext itself doesn't use this. + /// + /// TODO The alternative is to have a IDomainHelperAccessor singleton which is cached per UmbracoContext + /// + internal DomainHelper GetDomainHelper(ISiteDomainHelper siteDomainHelper) + => _domainHelper ?? (_domainHelper = new DomainHelper(PublishedSnapshot.Domains, siteDomainHelper)); /// /// Gets a value indicating whether the request has debugging enabled From aa46e1e2826cad696ca4534eb090ab30b0f3a11b Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 27 Apr 2018 17:36:22 +0200 Subject: [PATCH 54/97] Test nullref on NPocoSqlExtensions --- src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs index 7b12e2e69d..60172ef687 100644 --- a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs @@ -544,6 +544,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql SelectTop(this Sql sql, int count) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.SqlContext.SqlSyntax.SelectTop(sql, count); } @@ -554,6 +555,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql SelectCount(this Sql sql) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Select("COUNT(*)"); } @@ -569,6 +571,7 @@ namespace Umbraco.Core.Persistence /// public static Sql SelectCount(this Sql sql, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); var columns = fields.Length == 0 ? sql.GetColumns(withAlias: false) : fields.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); @@ -582,6 +585,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql SelectAll(this Sql sql) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Select("*"); } @@ -597,6 +601,7 @@ namespace Umbraco.Core.Persistence /// public static Sql Select(this Sql sql, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Select(sql.GetColumns(columnExpressions: fields)); } @@ -613,6 +618,7 @@ namespace Umbraco.Core.Persistence /// public static Sql Select(this Sql sql, string tableAlias, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Select(sql.GetColumns(tableAlias: tableAlias, columnExpressions: fields)); } @@ -628,6 +634,7 @@ namespace Umbraco.Core.Persistence /// public static Sql AndSelect(this Sql sql, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Append(", " + string.Join(", ", sql.GetColumns(columnExpressions: fields))); } @@ -645,6 +652,7 @@ namespace Umbraco.Core.Persistence /// public static Sql AndSelect(this Sql sql, string tableAlias, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Append(", " + string.Join(", ", sql.GetColumns(tableAlias: tableAlias, columnExpressions: fields))); } @@ -657,6 +665,8 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql Select(this Sql sql, Func, SqlRef> reference) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); + sql.Select(sql.GetColumns()); reference?.Invoke(new SqlRef(sql, null)); @@ -675,6 +685,8 @@ namespace Umbraco.Core.Persistence /// is added, so that it is possible to add (e.g. calculated) columns to the referencing Dto. public static Sql Select(this Sql sql, Func, SqlRef> reference, Func, Sql> sqlexpr) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); + sql.Select(sql.GetColumns()); sql = sqlexpr(sql); @@ -789,6 +801,7 @@ namespace Umbraco.Core.Persistence /// public static string Columns(this Sql sql, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return string.Join(", ", sql.GetColumns(columnExpressions: fields, withAlias: false)); } @@ -805,6 +818,7 @@ namespace Umbraco.Core.Persistence /// public static string Columns(this Sql sql, string alias, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return string.Join(", ", sql.GetColumns(columnExpressions: fields, withAlias: false, tableAlias: alias)); } From 8f13fa3ae522d6dea645645a93a7ea0e3bfcc7bc Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 27 Apr 2018 17:36:50 +0200 Subject: [PATCH 55/97] ISystemDefaultCultureProvider --- .../Implement/LanguageRepository.cs | 4 ---- .../PublishedContentCacheTests.cs | 2 +- .../Scoping/ScopedNuCacheTests.cs | 4 +++- .../TestHelpers/TestWithDatabaseBase.cs | 7 ++++++ .../TestSystemDefaultCultureProvider.cs | 9 +++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 5 ++-- .../Web/Mvc/UmbracoViewPageTests.cs | 4 +++- .../ISystemDefaultCultureProvider.cs | 13 ++++++++++ .../NuCache/PublishedSnapshotService.cs | 5 +++- .../SystemDefaultCultureProvider.cs | 24 +++++++++++++++++++ .../XmlPublishedCache/DomainCache.cs | 4 ++-- .../PublishedSnapshotService.cs | 11 ++++++--- .../XmlPublishedCache/XmlCacheComponent.cs | 1 + .../Runtime/WebRuntimeComponent.cs | 3 +++ src/Umbraco.Web/Umbraco.Web.csproj | 2 ++ 15 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs create mode 100644 src/Umbraco.Web/PublishedCache/ISystemDefaultCultureProvider.cs create mode 100644 src/Umbraco.Web/PublishedCache/SystemDefaultCultureProvider.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 8e42ee460b..1ca91d8774 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -256,10 +256,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // do NOT leak that language, it's not deep-cloned! private ILanguage GetDefault() { - // FIXME - var temp = SqlContext.Sql(); - if (temp == null) return null; - // get all cached, non-cloned var all = TypedCachePolicy.GetAllCached(PerformGetAll); diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 234b31d5a0..8212243e26 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -62,7 +62,7 @@ namespace Umbraco.Tests.Cache.PublishedCache _xml.LoadXml(GetXml()); var xmlStore = new XmlStore(() => _xml, null, null, null); var cacheProvider = new StaticCacheProvider(); - var domainCache = new DomainCache(ServiceContext.DomainService, ServiceContext.LocalizationService); + var domainCache = new DomainCache(ServiceContext.DomainService, SystemDefaultCultureProvider); var publishedShapshot = new Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedSnapshot( new PublishedContentCache(xmlStore, domainCache, cacheProvider, globalSettings, new SiteDomainHelper(), ContentTypesCache, null, null), new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, cacheProvider, ContentTypesCache), diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 06c13b2733..cc9813cdbd 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -90,7 +90,9 @@ namespace Umbraco.Tests.Scoping publishedSnapshotAccessor, Logger, ScopeProvider, - documentRepository, mediaRepository, memberRepository, Container.GetInstance(), new SiteDomainHelper()); + documentRepository, mediaRepository, memberRepository, + SystemDefaultCultureProvider, + Container.GetInstance(), new SiteDomainHelper()); } protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 070166eaff..0fcd3c9295 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -32,6 +32,7 @@ using LightInject; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; +using Umbraco.Tests.Testing.Objects.AccessorsAndProviders; namespace Umbraco.Tests.TestHelpers { @@ -77,6 +78,7 @@ namespace Umbraco.Tests.TestHelpers Container.Register(); Container.Register(factory => PublishedSnapshotService); + Container.Register(factory => SystemDefaultCultureProvider); Container.GetInstance() .Clear() @@ -229,12 +231,16 @@ namespace Umbraco.Tests.TestHelpers } } + protected ISystemDefaultCultureProvider SystemDefaultCultureProvider { get; set; } + protected IPublishedSnapshotService PublishedSnapshotService { get; set; } protected override void Initialize() // fixme - should NOT be here! { base.Initialize(); + SystemDefaultCultureProvider = new TestSystemDefaultCultureProvider(); + CreateAndInitializeDatabase(); // ensure we have a PublishedSnapshotService @@ -264,6 +270,7 @@ namespace Umbraco.Tests.TestHelpers ScopeProvider, cache, publishedSnapshotAccessor, Container.GetInstance(), Container.GetInstance(), Container.GetInstance(), + SystemDefaultCultureProvider, Logger, Container.GetInstance(), new SiteDomainHelper(), ContentTypesCache, diff --git a/src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs b/src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs new file mode 100644 index 0000000000..f7e5484500 --- /dev/null +++ b/src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs @@ -0,0 +1,9 @@ +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.Testing.Objects.AccessorsAndProviders +{ + public class TestSystemDefaultCultureProvider : ISystemDefaultCultureProvider + { + public string DefaultCulture { get; set; } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index eaec8a24ff..20bc181017 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -180,6 +180,7 @@ + @@ -595,9 +596,7 @@ - - - + diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index afc461c6de..295f42fee2 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -19,6 +19,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.AccessorsAndProviders; using Umbraco.Web; using Umbraco.Web.Models; using Umbraco.Web.Mvc; @@ -424,7 +425,8 @@ namespace Umbraco.Tests.Web.Mvc var scopeProvider = TestObjects.GetScopeProvider(Mock.Of()); var factory = Mock.Of(); _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(), null, - null, null, null, + null, null, null, + new TestSystemDefaultCultureProvider(), Current.Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), null, true, false); // no events var http = GetHttpContextFactory(url, routeData).HttpContext; diff --git a/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureProvider.cs b/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureProvider.cs new file mode 100644 index 0000000000..022b6d3e25 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureProvider.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Web.PublishedCache +{ + /// + /// Provides the system default culture. + /// + public interface ISystemDefaultCultureProvider + { + /// + /// Gets the system default culture. + /// + string DefaultCulture { get; } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 923502bea2..b498a1a42b 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -44,6 +44,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; private readonly ISiteDomainHelper _siteDomainHelper; + private readonly ISystemDefaultCultureProvider _systemDefaultCultureProvider; // volatile because we read it with no lock private volatile bool _isReady; @@ -83,6 +84,7 @@ namespace Umbraco.Web.PublishedCache.NuCache ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap, IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, + ISystemDefaultCultureProvider systemDefaultCultureProvider, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) : base(publishedSnapshotAccessor) { @@ -97,6 +99,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _documentRepository = documentRepository; _mediaRepository = mediaRepository; _memberRepository = memberRepository; + _systemDefaultCultureProvider = systemDefaultCultureProvider; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; @@ -1014,7 +1017,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var memberTypeCache = new PublishedContentTypeCache(null, null, _serviceContext.MemberTypeService, _publishedContentTypeFactory, _logger); - var defaultCulture = _serviceContext.LocalizationService.GetDefaultLanguageIsoCode(); // capture - fast + var defaultCulture = _systemDefaultCultureProvider.DefaultCulture; var domainCache = new DomainCache(domainSnap, defaultCulture); var domainHelper = new DomainHelper(domainCache, _siteDomainHelper); diff --git a/src/Umbraco.Web/PublishedCache/SystemDefaultCultureProvider.cs b/src/Umbraco.Web/PublishedCache/SystemDefaultCultureProvider.cs new file mode 100644 index 0000000000..6838d483b0 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/SystemDefaultCultureProvider.cs @@ -0,0 +1,24 @@ +using Umbraco.Core.Services; + +namespace Umbraco.Web.PublishedCache +{ + /// + /// Provides the default implementation of . + /// + public class SystemDefaultCultureProvider : ISystemDefaultCultureProvider + { + private readonly ILocalizationService _localizationService; + + /// + /// Initializes a new instance of the class. + /// + /// + public SystemDefaultCultureProvider(ILocalizationService localizationService) + { + _localizationService = localizationService; + } + + /// + public string DefaultCulture => _localizationService.GetDefaultLanguageIsoCode(); // capture - fast + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs index 64c1d80852..9a82840024 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs @@ -11,10 +11,10 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { private readonly IDomainService _domainService; - public DomainCache(IDomainService domainService, ILocalizationService localizationService) + public DomainCache(IDomainService domainService, ISystemDefaultCultureProvider systemDefaultCultureProvider) { _domainService = domainService; - DefaultCulture = localizationService.GetDefaultLanguageIsoCode(); // capture - fast + DefaultCulture = systemDefaultCultureProvider.DefaultCulture; } public IEnumerable GetAll(bool includeWildcards) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs index 1f327f4cd3..3b9a97d8cc 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly IUserService _userService; private readonly ICacheProvider _requestCache; private readonly IGlobalSettings _globalSettings; - private readonly ILocalizationService _localizationService; + private readonly ISystemDefaultCultureProvider _systemDefaultCultureProvider; private readonly ISiteDomainHelper _siteDomainHelper; #region Constructors @@ -45,6 +45,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, + ISystemDefaultCultureProvider systemDefaultCultureProvider, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, @@ -52,6 +53,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache bool testing = false, bool enableRepositoryEvents = true) : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, publishedSnapshotAccessor, documentRepository, mediaRepository, memberRepository, + systemDefaultCultureProvider, logger, globalSettings, siteDomainHelper, null, mainDom, testing, enableRepositoryEvents) { } @@ -62,6 +64,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache ICacheProvider requestCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, + ISystemDefaultCultureProvider systemDefaultCultureProvider, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, @@ -70,6 +73,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache bool testing, bool enableRepositoryEvents) : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), publishedSnapshotAccessor, documentRepository, mediaRepository, memberRepository, + systemDefaultCultureProvider, logger, globalSettings, siteDomainHelper, contentTypeCache, mainDom, testing, enableRepositoryEvents) { } @@ -80,6 +84,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, + ISystemDefaultCultureProvider systemDefaultCultureProvider, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, @@ -101,7 +106,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _memberService = serviceContext.MemberService; _mediaService = serviceContext.MediaService; _userService = serviceContext.UserService; - _localizationService = serviceContext.LocalizationService; + _systemDefaultCultureProvider = systemDefaultCultureProvider; _requestCache = requestCache; _globalSettings = globalSettings; @@ -146,7 +151,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // the current caches, but that would mean creating an extra cache (StaticCache // probably) so better use RequestCache. - var domainCache = new DomainCache(_domainService, _localizationService); + var domainCache = new DomainCache(_domainService, _systemDefaultCultureProvider); return new PublishedSnapshot( new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _siteDomainHelper, _contentTypeCache, _routesCache, previewToken), diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs index 37f29cc1d6..c9794eb99a 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs @@ -32,6 +32,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index a0c482075a..54afd0ad75 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -69,6 +69,9 @@ namespace Umbraco.Web.Runtime //it still needs to use the install controller so we can't do that composition.Container.RegisterFrom(); + // register the system culture provider + composition.Container.RegisterSingleton(); + var typeLoader = composition.Container.GetInstance(); var logger = composition.Container.GetInstance(); var proflog = composition.Container.GetInstance(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 1339166185..a39495430b 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -343,6 +343,7 @@ + @@ -387,6 +388,7 @@ + From a26d7747b6825e4167cb9b1232f99c757af05274 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 30 Apr 2018 15:51:05 +1000 Subject: [PATCH 56/97] removes commented out code --- src/Umbraco.Web/PublishedContentExtensions.cs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index ace05c33f5..e4a3a85d13 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1228,24 +1228,6 @@ namespace Umbraco.Web #region Culture - //TODO: Not used - ///// - ///// Gets the culture that would be selected to render a specified content, - ///// within the context of a specified current request. - ///// - ///// The content. - ///// The request Uri. - ///// The culture that would be selected to render the content. - //public static CultureInfo GetCulture(this IPublishedContent content, Uri current = null) - //{ - // return Models.ContentExtensions.GetCulture(UmbracoContext.Current, - // Current.Services.DomainService, - // Current.Services.LocalizationService, - // Current.Services.ContentService, - // content.Id, content.Path, - // current); - //} - /// /// Return the URL name for the based on the culture specified or default culture defined /// From 954098eb7e9a2d81ee78c374e36250dda83dd3ad Mon Sep 17 00:00:00 2001 From: Warren Date: Mon, 30 Apr 2018 13:52:05 +0100 Subject: [PATCH 57/97] Comments out & leaves comment about removing the open in split view link (otherwise we will forget) --- .../src/views/components/editor/umb-editor-header.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index 2ce16c5609..41d44114a0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -61,7 +61,10 @@ {{variant.language.name}} {{variant.state}} + + From 51d608125d5254b6f4a54d0858d0710b0f70cc2b Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 30 Apr 2018 23:29:44 +1000 Subject: [PATCH 58/97] updates editor config file with vs specific rules --- .editorconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.editorconfig b/.editorconfig index 5f3b4d684a..d2f3002c12 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,16 @@ indent_size = 4 # Trim trailing whitespace, limited support. # https://github.com/editorconfig/editorconfig/wiki/Property-research:-Trim-trailing-spaces trim_trailing_whitespace = true + +[*.{cs,vb}] +dotnet_style_predefined_type_for_locals_parameters_members = true:error + +dotnet_naming_rule.private_members_with_underscore.symbols = private_fields +dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore +dotnet_naming_rule.private_members_with_underscore.severity = suggestion + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +dotnet_naming_style.prefix_underscore.capitalization = camel_case +dotnet_naming_style.prefix_underscore.required_prefix = _ \ No newline at end of file From 9a2797303bdfa14a7a18003cbc1a3ee95b8b064f Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 30 Apr 2018 23:30:08 +1000 Subject: [PATCH 59/97] cleaning up files --- build/NuSpecs/tools/trees.config.install.xdt | 3 - src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 13 - .../Umbraco/Images/editor/dictionaryItem.gif | Bin 1040 -> 0 bytes .../Umbraco/Images/editor/help.png | Bin 377 -> 0 bytes .../Images/editor/insChildTemplateNew.gif | Bin 914 -> 0 bytes .../Umbraco/Images/editor/insField.gif | Bin 648 -> 0 bytes .../Umbraco/Images/editor/insFieldByLevel.gif | Bin 626 -> 0 bytes .../Umbraco/Images/editor/insFieldByTree.gif | Bin 621 -> 0 bytes .../Umbraco/Images/editor/insMacro.gif | Bin 610 -> 0 bytes .../Umbraco/Images/editor/insMacroSB.png | Bin 474 -> 0 bytes .../Umbraco/Images/editor/insMemberItem.gif | Bin 627 -> 0 bytes .../Umbraco/Images/editor/insRazorMacro.png | Bin 529 -> 0 bytes .../Umbraco/Images/editor/inshtml.GIF | Bin 191 -> 0 bytes .../Images/editor/masterpageContent.gif | Bin 132 -> 0 bytes .../Images/editor/masterpagePlaceHolder.gif | Bin 286 -> 0 bytes .../Umbraco/Images/editor/xslVisualize.gif | Bin 663 -> 0 bytes .../config/trees.Release.config | 3 +- src/Umbraco.Web.UI/config/trees.config | 3 +- .../umbraco/config/create/UI.Release.xml | 21 - .../umbraco/config/create/UI.xml | 21 - .../umbraco/create/PartialViewMacro.ascx | 38 -- .../umbraco/create/PartialViewMacro.ascx.cs | 79 --- .../create/PartialViewMacro.ascx.designer.cs | 87 ---- src/Umbraco.Web.UI/umbraco/create/User.ascx | 35 -- .../umbraco/create/User.ascx.cs | 82 ---- .../umbraco/create/User.ascx.designer.cs | 105 ---- .../umbraco/create/language.ascx | 18 - src/Umbraco.Web.UI/umbraco/create/script.ascx | 34 -- src/Umbraco.Web.UI/umbraco/create/xslt.ascx | 30 -- .../umbraco/create/xslt.ascx.cs | 11 - .../umbraco/create/xslt.ascx.designer.cs | 15 - src/Umbraco.Web.UI/umbraco/css/background.gif | Bin 151 -> 0 bytes .../umbraco/css/permissionsEditor.css | 43 -- src/Umbraco.Web.UI/umbraco/css/splitter.gif | Bin 88 -> 0 bytes src/Umbraco.Web.UI/umbraco/css/umbracoGui.css | 455 ------------------ .../umbraco/users/EditUser.aspx | 14 - .../umbraco/users/EditUser.aspx.cs | 11 - .../umbraco/users/EditUser.aspx.designer.cs | 15 - .../umbraco/users/EditUserType.aspx | 27 -- .../umbraco/users/NodePermissions.ascx | 35 -- .../umbraco/users/PermissionEditor.aspx | 47 -- .../umbraco/users/PermissionsEditor.js | 144 ------ .../umbraco/users/PermissionsHandler.asmx | 1 - src/Umbraco.Web/Trees/XsltTreeController.cs | 69 --- src/Umbraco.Web/Umbraco.Web.csproj | 1 - 45 files changed, 2 insertions(+), 1458 deletions(-) delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/dictionaryItem.gif delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/help.png delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/insChildTemplateNew.gif delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/insField.gif delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/insFieldByLevel.gif delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/insFieldByTree.gif delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/insMacro.gif delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/insMacroSB.png delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/insMemberItem.gif delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/insRazorMacro.png delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/inshtml.GIF delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/masterpageContent.gif delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/masterpagePlaceHolder.gif delete mode 100644 src/Umbraco.Web.UI/Umbraco/Images/editor/xslVisualize.gif delete mode 100644 src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx delete mode 100644 src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.designer.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/create/User.ascx delete mode 100644 src/Umbraco.Web.UI/umbraco/create/User.ascx.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/create/User.ascx.designer.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/create/language.ascx delete mode 100644 src/Umbraco.Web.UI/umbraco/create/script.ascx delete mode 100644 src/Umbraco.Web.UI/umbraco/create/xslt.ascx delete mode 100644 src/Umbraco.Web.UI/umbraco/create/xslt.ascx.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/create/xslt.ascx.designer.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/css/background.gif delete mode 100644 src/Umbraco.Web.UI/umbraco/css/permissionsEditor.css delete mode 100644 src/Umbraco.Web.UI/umbraco/css/splitter.gif delete mode 100644 src/Umbraco.Web.UI/umbraco/css/umbracoGui.css delete mode 100644 src/Umbraco.Web.UI/umbraco/users/EditUser.aspx delete mode 100644 src/Umbraco.Web.UI/umbraco/users/EditUser.aspx.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/users/EditUser.aspx.designer.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/users/EditUserType.aspx delete mode 100644 src/Umbraco.Web.UI/umbraco/users/NodePermissions.ascx delete mode 100644 src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx delete mode 100644 src/Umbraco.Web.UI/umbraco/users/PermissionsEditor.js delete mode 100644 src/Umbraco.Web.UI/umbraco/users/PermissionsHandler.asmx delete mode 100644 src/Umbraco.Web/Trees/XsltTreeController.cs diff --git a/build/NuSpecs/tools/trees.config.install.xdt b/build/NuSpecs/tools/trees.config.install.xdt index 5a549e3fd8..dc59b5db10 100644 --- a/build/NuSpecs/tools/trees.config.install.xdt +++ b/build/NuSpecs/tools/trees.config.install.xdt @@ -86,9 +86,6 @@ - create.aspx - - xslt.ascx - ASPXCodeBehind - - - xslt.ascx - UserControlProxy.aspx ASPXCodeBehind @@ -573,13 +566,7 @@ - - UserControl - - - UserControl - diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/dictionaryItem.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/dictionaryItem.gif deleted file mode 100644 index e9dc737967820c2eacff5ccae0a6b8ddc60f6d3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1040 zcmeH`YfF=106^buUJDBm%tCC6go*@Ff@#nyHOmmAK8TIb5VbHcA^l)p*w!p4%?wPi zT1=aoQ)kn;T&8Z`CL21OCf>F=-PO%)y1m!uE&CIF>HL8M2hQ~y8Oaw5!l4t8j|HVM zE4qi3Y0S#Bdll)-N&=AW0}Aoj{PZe3w+^c}k&bVZ8Uyl0JjEButu7qHNw=3!X?6So z#kK=?>=QjwW%uyxx&;*sPPGU|1cTF{mkWA0ptD-F2@^sNij#VSS!ctXc9(J;UfxDl zc2JU^ota;?X~hs6^p4COAYgQM*^2-~#zBR}mL0RxWbvZFsr`u);QQp_veCDsKY(p| zzDV(`q5XM_&^j+$&?A_nrJVbrPo{!VAo`*5YWiRd_)tLfy)e6Q1Os*t?lJ9PIB+=j zJuo*0~?qjZT7dwCn(8JzakQO|B-S|oTsQTUBXp>J;v#9eR z!iRP@?)DLS8)mfmfS}5}hr7IlacRzG)U9k-@vr%SBv;L7w`6u`LPK;hbBbzB+SzR< zzGg<%n=mp^u2}IA{Uu(9-MLTL+}N)j)Z)Sy^f37s=s*9WKs4efcsYql<=NzexQ$*{|74oPz zOWx=`yzEvQEsxc9HNF00LiLb%yo^z&rbahhil<%+h)Sv9L?po?M)R?}x>sG43`KB! zI22i$15pFxZWZ-3u?<-br^|9rU4RN^qGGe=-@8KDA-v-!%hgG5{W;CCxpzB5<%}yY z1MVEnE@|R&>d)Wa&;{}76qf&kkd$PkZXzi4OvIY2t+;-T6%d35?DVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5XBm#UwTz!20PnmMc$7k99|NmQB z{x4g$?8S>0K=ww{#dm>fI7)*2f`MERV7S-3Hxejz+0(@_q~cam!f`GJrVhs41`Z4l z1=u(gWDYSfwm3^DG(0$zqvLSk7~4}u76G3bt*wkK5|WBB4Gc*R9h)|7I&_o~s4I&p zV9LyyGpBYo3g+-RFgUh2su>tJ3k1lhFbFtsB!*4k5oQ*S02(H6NFgw6Mpgqu6SuLW ztKup~CJv=D9FL3}p3G1HTF6q$sqAAad_)z*G*=Fgd19xwVmDCHGjBIHHGzhoLO>%D u4{+F7Q^!T|g{Kw7&1_p-z z|NjHYQ7{?;Lm~tef3h$#Fi0`zfLsg86AT>r45l119vc=MY~~QwiaD`i;o%kmm4FQm z4;LN)3nTyBj+{C!d(emK<~? rAUNUVWKG_CcOnW>Pfus7?NZ4UTIw}T&pPYwsjaK8uV-auVz34Pz2|1g diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insField.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/insField.gif deleted file mode 100644 index 3a3721dd352923b0dd762d8144f383770eb9f040..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 648 zcmZ?wbhEHb6k`x)cvi&l@83UD&*aIAwm*IQ```Ds|IWAn`u%t6lATL89li7X*X2h) z-+un{|3vxUm$h#`{dxQ8&;Q@YAMOwS@?!nvhd=Lc)BE!M@Bd{=pTGY-aP{k~6?^}$ zHU9r`^118JfByb^==9D1pQoL={r$lX!-q!_YkHUbcsYC8(p}Gv#lF4K^M8Thx7S;~ zznJy;K$^U;J zwhzvkzHGN&V%3w~CjUQ9czh`0&l1Hick@5r$@zC}#-A%Qeyn%?v&s42tJ6>3{(k=a z)xVE--rlUS^v!s5w*TG5{l%`*S`%T=MsITYORX^8=O}_nr&SY?{7o z*X?y`3)UU_|FP%hDy0{XZnaHY`}hBc{|D2ypZ_{{_5PJxPrQG2YvH;>4~`TRwaoed zVe-#cvp?S2`S0a)ARlIiR=@nZ~{Qv)-fjFS}lZBCip@=~Tq#P6{4D3%E zikg~RTHD$?I=i}?($%w~EP{M{nu8T%?CnGhrZ%a_3m93-dð1cXM|rP9$Nm<7V_vmP5yuk@r_P*fHdNvb%XKnmm6CF2 z;bZ2EzTKkFz~U^%%EH5{U9)A-LdDbGvrI2?YaZ f<->i@83UD&*aIAwm*IQo8gS{uit;CF4_6{K6EzYW*ymsg5?=NP3xtss<)$E@yCeK~H|Nrm9&rU`^ zI8yNO;gY|v+uq*j`Tt{ocxF?4QTMc^yZ(Qe?3Y;eWVgw)W3fM8&Yrb$-<=~BE4Q9# zo3{4<$DVJmw>)|K`}qM&``{c)-;B36YwmA1_khd`6;EHb>(8s^d29B6e!8}(WzM6s{r^8sSh()czwd7!9!ad}UGnbk%Tc4~RrOGF z3+idsj<$9WmWi0!WG2rapds&Q6&|Q)pl|GB@6hTcCFg1*Wx}v?7b}MgW2>^bo*cip z&aRyzd;&cCTNIplO^sw!*tvF{I&-esB8=D0&spdMHaE8M7k{+!xTth2R!HSb#D#^- s({-2((oQUJb>U+Rndjq?A~9p8ol2_Nkb{b`tARR$we)5zQ33?clG{->kj>S)%@Z9oPXcn&Retp%iVnY;GF*-C#>9h;>p|J zcaBut-)^vC>xqX)692w#dwZj2`}wcam+hXuY?pgf@weAo9vmt7{B-S)m$M)2F#P{x z|IJlO|NsAIpd(QH$->CM5Y3GqMMpqTHD$?I=i}?LjBduzr+}a^ z&;AxuZy7@y30DsGU8l~RYxeS$(R8#_JmJjB#AbD;Mg0WBb%y6J>|ZzQzGGnd#3si3 zwTbyotgu5sBdg~BZ7g@TI?QY05)o2~nbCdyK)ay&yQ&Wgos679We$=C7aNE diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insMacro.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/insMacro.gif deleted file mode 100644 index eeb3cdb444ac6581d41b75834279ecf941fbd9f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 610 zcmZ?wbhEHb6k`x)c*el+{Q1kbpZbFz3zh8d% zbLG|(Z$AAwaP{lrO($lp*t>k&nYpX?FIabI;krXhHyvHE^~B{zKiBR&y>aikL#J;( zdHehA=Rcpn|26eYp1f%L^kuuJE!{PB$n^m$|Gmo3p>wk3YqnelaR!MB6u-wsuOJ5u-USj+blZQoCJeLvOn{dE8Lvy*?E zpZ??GoS#>h{Jg&M=dJa>?rs0|V9)Qzhkidj_WSAafB*jd|Noz1pn>8~7Dfh!5C$EP zLqKujz`nmBq^Y^3wXIbkxU0LHSw>X0Jw#4YLY!MfSe0L1QBH2+q(zIH6uL~c^qIAF zELg<&7cSYnWvfO|V1U1$uaCEvr-z%yB&A?I(;iDhV-}%Jt#ZMZ!F@bzY+S6oiWj!t zfAH|pW)1geZZ1v^_I9>5)>f0Ay1P0%zIku;>qt9aPmdZ)&VmQI7dkZ6V^(Z1WOd_` zWZ-bo(C~=j7FTDB*?q97ja5iQT~kUxp@EHwQ%J#r!AocbpF#4e7Rz0004@NklPr%n+>$8H^hPNon+hbT)*dQnR%Evd}PvaQUQR6$J(}Q^-Akyd+A`%z6$Pn^)tv}By82qf%f3iUR3>PVpnO4r(|4{7 zwtG(5W}Ngxy`K3COFZPb1)t$jq3#!gG2vIZg z`8Nj7uN`ZHr=9zacBCkwZW51{@N%GjJ0u)<`n(- zQt--G@Ig?DKOdSHY<2DHgXt`rPrnY+q#tD@vw4Iv0;U`Ro$wFJ6OiVGfb{occM?k| zBf_5&K-X)Ax%dsXf=O%y;&79DO0U!+Ieq*3F`KE?b-|3CKp|FPfFH)GbyeSV2mfB%1&w`PA< z_2joVdLA4pIB@mbqqF@V?$7!E`|z`4v5yZ$e7T#y{ruOzuiI{}Qu=%+XTiEdpTGZI zvGv5XrMv!rm>izjRMWd;;krW`_nv$5_V>q!OV;i@{r2AC>C1LcU$*Q2#|ieqIqp%# zcaBuNy;<{MhvB=sGrzx>_4(=A=LalruT%T+a`vBB&EH;cX`8n8=d0QGw;Md!ZSwF) z;<@Y3|NsBbKu4hXlZBCiA(}x4WG5(27}zf~L^n0Jw6?W(bar(&>1c-q%IoX(G&}hy z8@WsPPHhU3mhm@`_71g^(^9lm_w-P0H4~TA2oaB9*tv_>(u%Rwk6%SnhTnA8P99+q z?)@z;;ZlY+62=TnOeasDZS{(j3J5lrWfBy;$;QfQajsSE1QRne)5}*1Z(FQST>tWw zOY{euV1v59Ln9NHhyH`=I2JW9Mu2fjrGiNgM%QrJ;80I6+%%$LtnjzeF;&>tof}4fM(S`H9 zRxshjqf?8s`B>nw85gEZDYwBf7mbILj_|;*H=O{l0{k1`uXIvgr8Q@(L8D5Cnieg} z8oE$a%_6s~2^ocTNXx52a#lH#(uG&-0YM^l9oYdl7qasw0pk4{e@c|4@uYC{ZP6y!7$3VQP z#r1j);9=<;5S~of0T|jLtS)^C{q_*lAH_*pY4=qurWc{DPWKk$${v+N2>JY_Z T!Jt=y00000NkvXXu0mjfyIlNb diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/inshtml.GIF b/src/Umbraco.Web.UI/Umbraco/Images/editor/inshtml.GIF deleted file mode 100644 index 3442c48cd48c9fdb78d08459a533baef47997cdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 191 zcmZ?wbhEHb6k`x)*v!E2|NsB$>gtx3mIDV4R5LKN07XE8KvoNc0t+y}f#OdVMg|6E z1|5(H$P5RToPractGB8*oV8B4zMxVig0-E)!%?CyC4E;(ZZgBH<=-c;F&#@ZcF5s5 z7U{~Mp<>3!9P;sHFVz3j_fxPWUvMRMVdi) diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpageContent.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpageContent.gif deleted file mode 100644 index ad993382348565d4f7c7acdee5121d833c70bc9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132 zcmZ?wbhEHb6k`x)Sj586($aG5*f9nMhW}syWI%x8PZmZ71{MY#5ErDDf!Whz*PVX` zr#!U<8D5_;WN{YgOVV_3@?3Xd`zkfA{m#D|jBD&P*WTirqrav2OObMda|e%z^znt0 Z4t}wf417{@@nzn+X&Wkh^t^=_tN{TBF7^Nb diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpagePlaceHolder.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpagePlaceHolder.gif deleted file mode 100644 index 00de9bed9ad59806e696ccad106b3f608e16cbf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmZ?wbhEHb6k`x)I3mWdbm`Kbo}RsX_pV*Lwxy+|y1M$$Pjwrc9Y~ z?%cWW-~WIA|Ns5>|IeR4zjp8W^Y=t=Vsbbj&ZE|)?%Z0yEF;3`$xUry@sI#Z?ciQrZ@QJW!usc-Ti^73s~Zhm@J&d&XZ9=&+|^yTZ0iBns8CoWmH zsek&cY4aBDIdo+D{Dtc_ZGQgx&88hYCQYAy<@)t|_wVmNa`@oUgW-{(D<;JLJ(l=( zbNHqi`DN+;lZ%}f_7z2k2R>im{$z&Zg?iJy@=g*(tzkmPw_3P))pFe*5`2PL-w{PFReEIU>!-x0p-@kkJ?(N&RZ{ECl{rdH* zSFc{aeEH(Vi|5auKYRA<>C>lAo;=yKY177y8#ip&uzvmeb?erxUAuP8nl-CeuU@rk zRd;uHRaI4DVxotK2QVbTAxZ+F_>+Z^fgztk2c!oSCk*Ug8uFW(TUy)NJK9sbds0*S zlarF#(-@~spTRh}EzLV1z;B_ix3?c-a(lW@c2stBc6MZTh<8eRhHp$xY+O!!Y)+`p zidLTtznI+kgv8vq+%Ugwt=?Gyd9itM@#gwkngOXkN3#OspT`^N8LDf`%zYB{!c2{k zLxfk7>v7A~PtSD&?(9BrfT>gU=p~Pfn_9WM1w~{WA_5p$4mojcO*zS=e1wHlP_DyZ z!jTJ-nh` - @@ -35,4 +34,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index b647bcbcb6..3076936cd5 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -20,7 +20,6 @@ - @@ -39,4 +38,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml index 1a082ab916..69983469ae 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml @@ -59,13 +59,6 @@ - -
    XSLT file
    - /create/xslt.ascx - - - -
    member
    /create/member.ascx @@ -110,20 +103,6 @@
    - -
    Language
    - /create/language.ascx - - - -
    - -
    Language
    - /create/language.ascx - - - -
    diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml index 46c93c6ede..a1bddce17e 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml @@ -68,13 +68,6 @@ - -
    XSLT file
    - /create/xslt.ascx - - - -
    membergroup
    /create/simple.ascx @@ -105,20 +98,6 @@
    - -
    Language
    - /create/language.ascx - - - -
    - -
    Language
    - /create/language.ascx - - - -
    diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx deleted file mode 100644 index 99932fc2fe..0000000000 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx +++ /dev/null @@ -1,38 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PartialViewMacro.ascx.cs" Inherits="Umbraco.Web.UI.Umbraco.Create.PartialViewMacro" %> -<%@ Import Namespace="umbraco" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> -<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> - - - - - * - - Cannot end with '/' or '.' - - - - - - - - - - - - - - -
    [DataMember(Name = "name")] public string Name { get; set; } - + [DataMember(Name = "state")] public string PublishedState { get; set; } @@ -33,6 +33,9 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "exists")] public bool Exists { get; set; } + [DataMember(Name = "isEdited")] + public bool IsEdited { get; set; } + /// /// Determines if this is the variant currently being edited /// diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs index 07b64ac309..26b4332bca 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs @@ -36,6 +36,7 @@ namespace Umbraco.Web.Models.Mapping Name = source.GetName(x.IsoCode), Exists = source.IsCultureAvailable(x.IsoCode), // segments ?? PublishedState = source.PublishedState.ToString(), + IsEdited = source.IsCultureEdited(x.IsoCode) //Segment = ?? We'll need to populate this one day when we support segments }).ToList(); From 6db5ebf6f9d1fcc1502edb3f98876bbef0df3029 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 1 May 2018 00:15:05 +1000 Subject: [PATCH 61/97] More xslt removal --- src/Umbraco.Core/Macros/MacroTagParser.cs | 2 +- src/Umbraco.Core/Macros/XsltExtension.cs | 17 - .../Macros/XsltExtensionAttribute.cs | 33 - .../Macros/XsltExtensionCollection.cs | 12 - .../Macros/XsltExtensionCollectionBuilder.cs | 78 -- src/Umbraco.Core/Models/IMacro.cs | 19 +- src/Umbraco.Core/Models/Macro.cs | 36 +- src/Umbraco.Core/Models/MacroTypes.cs | 4 - src/Umbraco.Core/Persistence/Dtos/MacroDto.cs | 10 +- .../Persistence/Factories/MacroFactory.cs | 4 +- .../Persistence/Mappers/MacroMapper.cs | 2 - .../Services/EntityXmlSerializer.cs | 2 - .../Services/Implement/MacroService.cs | 3 - .../Services/Implement/PackagingService.cs | 4 +- src/Umbraco.Core/Umbraco.Core.csproj | 4 - .../Composing/TypeFinderTests.cs | 64 -- .../Composing/TypeLoaderTests.cs | 15 +- .../Composing/XsltExtensionCollectionTests.cs | 41 -- src/Umbraco.Tests/Macros/MacroTests.cs | 1 - src/Umbraco.Tests/Models/MacroTests.cs | 2 +- .../Repositories/MacroRepositoryTest.cs | 22 +- .../Services/MacroServiceTests.cs | 6 +- .../Services/PackagingServiceTests.cs | 2 +- src/Umbraco.Tests/UI/LegacyDialogTests.cs | 1 - src/Umbraco.Tests/Umbraco.Tests.csproj | 1 - src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 1 - .../Umbraco/config/lang/zh_tw.xml | 4 +- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 - .../umbraco/config/lang/en_us.xml | 2 - src/Umbraco.Web.UI/umbraco/config/lang/es.xml | 2 - src/Umbraco.Web.UI/umbraco/config/lang/fr.xml | 2 - src/Umbraco.Web.UI/umbraco/config/lang/ja.xml | 2 - src/Umbraco.Web.UI/umbraco/config/lang/nl.xml | 2 - src/Umbraco.Web.UI/umbraco/config/lang/pl.xml | 2 - src/Umbraco.Web.UI/umbraco/config/lang/ru.xml | 2 - src/Umbraco.Web.UI/umbraco/config/lang/tr.xml | 2 - src/Umbraco.Web.UI/umbraco/config/lang/zh.xml | 2 - .../developer/Macros/EditMacro.aspx.cs | 4 +- .../Macros/EditMacro.aspx.designer.cs | 12 +- .../developer/Macros/assemblyBrowser.aspx | 28 - .../umbraco/developer/Macros/editMacro.aspx | 17 +- src/Umbraco.Web/Composing/Current.cs | 8 +- src/Umbraco.Web/CompositionExtensions.cs | 17 +- src/Umbraco.Web/Macros/MacroModel.cs | 7 - src/Umbraco.Web/Macros/MacroRenderer.cs | 39 +- src/Umbraco.Web/Macros/XsltMacroEngine.cs | 687 ------------------ .../ThumbnailProviderCollection.cs | 21 - .../ThumbnailProviderCollectionBuilder.cs | 15 - .../Runtime/WebRuntimeComponent.cs | 11 +- src/Umbraco.Web/TypeLoaderExtensions.cs | 32 +- src/Umbraco.Web/Umbraco.Web.csproj | 73 +- .../PackageInstance/PackagerUtility.cs | 3 - .../XsltExtensionAttribute.cs | 28 - .../umbraco/Trees/Trees.cd | 79 -- .../umbraco/Trees/XmlTree.xsd | 26 - .../umbraco/Trees/XmlTree.xsx | 3 - .../umbraco/create/XsltTasks.cs | 120 --- .../umbraco/create/xslt.ascx.cs | 127 ---- .../developer/Macros/assemblyBrowser.aspx | 29 - .../developer/Macros/assemblyBrowser.aspx.cs | 204 ------ .../Macros/assemblyBrowser.aspx.designer.cs | 69 -- .../developer/Macros/editMacro.aspx.cs | 70 +- .../umbraco/developer/Xslt/editXslt.aspx.cs | 198 ----- .../umbraco/developer/Xslt/getXsltStatus.asmx | 1 - .../developer/Xslt/getXsltStatus.asmx.cs | 88 --- .../developer/Xslt/xsltChooseExtension.aspx | 44 -- .../Xslt/xsltChooseExtension.aspx.cs | 164 ----- .../Xslt/xsltChooseExtension.aspx.designer.cs | 51 -- .../developer/Xslt/xsltInsertValueOf.aspx | 61 -- .../developer/Xslt/xsltInsertValueOf.aspx.cs | 55 -- .../Xslt/xsltInsertValueOf.aspx.designer.cs | 51 -- .../umbraco/developer/Xslt/xsltVisualize.aspx | 55 -- .../developer/Xslt/xsltVisualize.aspx.cs | 84 --- .../Xslt/xsltVisualize.aspx.designer.cs | 87 --- .../templateControls/InlineXslt.xsltTemplate | 14 - .../umbraco/templateControls/Item.cs | 37 +- .../umbraco/templateControls/ItemRenderer.cs | 50 +- .../templateControls/Resources.Designer.cs | 84 --- .../umbraco/templateControls/Resources.resx | 124 ---- .../umbraco/webservices/codeEditorSave.asmx | 1 - .../webservices/codeEditorSave.asmx.cs | 188 ----- 81 files changed, 48 insertions(+), 3528 deletions(-) delete mode 100644 src/Umbraco.Core/Macros/XsltExtension.cs delete mode 100644 src/Umbraco.Core/Macros/XsltExtensionAttribute.cs delete mode 100644 src/Umbraco.Core/Macros/XsltExtensionCollection.cs delete mode 100644 src/Umbraco.Core/Macros/XsltExtensionCollectionBuilder.cs delete mode 100644 src/Umbraco.Tests/Composing/XsltExtensionCollectionTests.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/developer/Macros/assemblyBrowser.aspx delete mode 100644 src/Umbraco.Web/Macros/XsltMacroEngine.cs delete mode 100644 src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderCollection.cs delete mode 100644 src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderCollectionBuilder.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/XsltExtensionAttribute.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/Trees/Trees.cd delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/Trees/XmlTree.xsd delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/Trees/XmlTree.xsx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/create/XsltTasks.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/create/xslt.ascx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.designer.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/editXslt.aspx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/getXsltStatus.asmx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/getXsltStatus.asmx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.designer.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.designer.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.designer.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/InlineXslt.xsltTemplate delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.Designer.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.resx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx.cs diff --git a/src/Umbraco.Core/Macros/MacroTagParser.cs b/src/Umbraco.Core/Macros/MacroTagParser.cs index 26f2b34ae5..857d36d2da 100644 --- a/src/Umbraco.Core/Macros/MacroTagParser.cs +++ b/src/Umbraco.Core/Macros/MacroTagParser.cs @@ -179,7 +179,7 @@ namespace Umbraco.Core.Macros // Check whether it's a single tag () or a tag with children (...) if (tag.Substring(tag.Length - 2, 1) != "/" && tag.IndexOf(" ") > -1) { - String closingTag = ""; + string closingTag = ""; // Tag with children are only used when a macro is inserted by the umbraco-editor, in the // following format: "", so we // need to delete extra information inserted which is the image-tag and the closing diff --git a/src/Umbraco.Core/Macros/XsltExtension.cs b/src/Umbraco.Core/Macros/XsltExtension.cs deleted file mode 100644 index 7bc9277ac3..0000000000 --- a/src/Umbraco.Core/Macros/XsltExtension.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Umbraco.Core.Macros -{ - /// - /// Encapsulates what an xslt extension object is when used for macros - /// - internal sealed class XsltExtension - { - public XsltExtension(string ns, object extensionObject) - { - Namespace = ns; - ExtensionObject = extensionObject; - } - - public string Namespace { get; private set; } - public object ExtensionObject { get; private set; } - } -} diff --git a/src/Umbraco.Core/Macros/XsltExtensionAttribute.cs b/src/Umbraco.Core/Macros/XsltExtensionAttribute.cs deleted file mode 100644 index 8cde062046..0000000000 --- a/src/Umbraco.Core/Macros/XsltExtensionAttribute.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Security.Permissions; -using System.Web; - -namespace Umbraco.Core.Macros -{ - /// - /// Allows App_Code XSLT extensions to be declared using the [XsltExtension] class attribute. - /// - /// - /// An optional XML namespace can be specified using [XsltExtension("MyNamespace")]. - /// - [AttributeUsage(AttributeTargets.Class)] - public class XsltExtensionAttribute : Attribute - { - public XsltExtensionAttribute() - { - Namespace = String.Empty; - } - - public XsltExtensionAttribute(string ns) - { - Namespace = ns; - } - - public string Namespace { get; set; } - - public override string ToString() - { - return Namespace; - } - } -} diff --git a/src/Umbraco.Core/Macros/XsltExtensionCollection.cs b/src/Umbraco.Core/Macros/XsltExtensionCollection.cs deleted file mode 100644 index 4d32382d06..0000000000 --- a/src/Umbraco.Core/Macros/XsltExtensionCollection.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Core.Composing; - -namespace Umbraco.Core.Macros -{ - internal class XsltExtensionCollection : BuilderCollectionBase - { - public XsltExtensionCollection(IEnumerable items) - : base(items) - { } - } -} diff --git a/src/Umbraco.Core/Macros/XsltExtensionCollectionBuilder.cs b/src/Umbraco.Core/Macros/XsltExtensionCollectionBuilder.cs deleted file mode 100644 index 45a7615e3d..0000000000 --- a/src/Umbraco.Core/Macros/XsltExtensionCollectionBuilder.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using LightInject; -using Umbraco.Core.Composing; - -namespace Umbraco.Core.Macros -{ - // that one is special since it's not initialized with XsltExtension types, but with Xslt extension object types, - // which are then wrapped in an XsltExtension object when the collection is created. so, cannot really inherit - // from (Lazy)CollectionBuilderBase and have to re-implement it. but almost everything is copied from CollectionBuilderBase. - - internal class XsltExtensionCollectionBuilder : ICollectionBuilder - { - private readonly IServiceContainer _container; - private readonly List>> _producers = new List>>(); - private readonly object _locker = new object(); - private ServiceRegistration[] _registrations; - - public XsltExtensionCollectionBuilder(IServiceContainer container) - { - _container = container; - - // register the collection - container.Register(_ => CreateCollection(), new PerContainerLifetime()); - } - - public static XsltExtensionCollectionBuilder Register(IServiceContainer container) - { - // register the builder - per container - var builderLifetime = new PerContainerLifetime(); - container.Register(builderLifetime); - return container.GetInstance(); - } - - public XsltExtensionCollectionBuilder AddExtensionObjectProducer(Func> producer) - { - lock (_locker) - { - if (_registrations != null) - throw new InvalidOperationException("Cannot configure a collection builder after its types have been resolved."); - _producers.Add(producer); - } - return this; - } - - private void RegisterTypes() - { - lock (_locker) - { - if (_registrations != null) return; - - var prefix = GetType().FullName + "_"; - var i = 0; - foreach (var type in _producers.SelectMany(x => x()).Distinct()) - { - var name = $"{prefix}{i++:00000}"; - _container.Register(type, type, name); - } - - _registrations = _container.AvailableServices - .Where(x => x.ServiceName.StartsWith(prefix)) - .OrderBy(x => x.ServiceName) - .ToArray(); - } - } - - public XsltExtensionCollection CreateCollection() - { - RegisterTypes(); // will do it only once - - var exts = _registrations.SelectMany(r => r.ServiceType.GetCustomAttributes(true) - .Select(a => new XsltExtension(a.Namespace.IfNullOrWhiteSpace(r.ServiceType.FullName), _container.GetInstance(r.ServiceType, r.ServiceName)))); - - return new XsltExtensionCollection(exts); - } - } -} diff --git a/src/Umbraco.Core/Models/IMacro.cs b/src/Umbraco.Core/Models/IMacro.cs index 1100212190..c56d3d9628 100644 --- a/src/Umbraco.Core/Models/IMacro.cs +++ b/src/Umbraco.Core/Models/IMacro.cs @@ -57,29 +57,14 @@ namespace Umbraco.Core.Models ///
    [DataMember] string ControlType { get; set; } - - /// - /// Gets or sets the name of the assembly, which should be used by the Macro - /// - /// Will usually only be filled if the ScriptFile is a Usercontrol - [DataMember] - [Obsolete("This is no longer used, we should remove it in v8, CustomControl macros are gone")] - string ControlAssembly { get; set; } - + /// /// Gets or set the path to the Python file in use /// /// Optional: Can only be one of three Script, Python or Xslt [DataMember] string ScriptPath { get; set; } - - /// - /// Gets or sets the path to the Xslt file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - string XsltPath { get; set; } - + /// /// Gets or sets a list of Macro Properties /// diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs index bd05abb1c1..6e72942b6e 100644 --- a/src/Umbraco.Core/Models/Macro.cs +++ b/src/Umbraco.Core/Models/Macro.cs @@ -35,13 +35,11 @@ namespace Umbraco.Core.Models /// /// /// - /// - /// /// /// /// /// - public Macro(int id, Guid key, bool useInEditor, int cacheDuration, string @alias, string name, string controlType, string controlAssembly, string xsltPath, bool cacheByPage, bool cacheByMember, bool dontRender, string scriptPath) + public Macro(int id, Guid key, bool useInEditor, int cacheDuration, string @alias, string name, string controlType, bool cacheByPage, bool cacheByMember, bool dontRender, string scriptPath) : this() { Id = id; @@ -51,8 +49,6 @@ namespace Umbraco.Core.Models Alias = alias.ToCleanString(CleanStringType.Alias); Name = name; ControlType = controlType; - ControlAssembly = controlAssembly; - XsltPath = xsltPath; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; @@ -67,16 +63,12 @@ namespace Umbraco.Core.Models /// /// /// - /// - /// /// /// /// /// public Macro(string @alias, string name, string controlType = "", - string controlAssembly = "", - string xsltPath = "", string scriptPath = "", bool cacheByPage = false, bool cacheByMember = false, @@ -90,8 +82,6 @@ namespace Umbraco.Core.Models Alias = alias.ToCleanString(CleanStringType.Alias); Name = name; ControlType = controlType; - ControlAssembly = controlAssembly; - XsltPath = xsltPath; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; @@ -125,9 +115,7 @@ namespace Umbraco.Core.Models public readonly PropertyInfo CacheByMemberSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByMember); public readonly PropertyInfo DontRenderSelector = ExpressionHelper.GetPropertyInfo(x => x.DontRender); public readonly PropertyInfo ControlPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ControlType); - public readonly PropertyInfo ControlAssemblySelector = ExpressionHelper.GetPropertyInfo(x => x.ControlAssembly); public readonly PropertyInfo ScriptPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ScriptPath); - public readonly PropertyInfo XsltPathSelector = ExpressionHelper.GetPropertyInfo(x => x.XsltPath); public readonly PropertyInfo PropertiesSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); } @@ -281,17 +269,6 @@ namespace Umbraco.Core.Models set { SetPropertyValueAndDetectChanges(value, ref _scriptFile, Ps.Value.ControlPathSelector); } } - /// - /// Gets or sets the name of the assembly, which should be used by the Macro - /// - /// Will usually only be filled if the ControlType is a Usercontrol - [DataMember] - public string ControlAssembly - { - get { return _scriptAssembly; } - set { SetPropertyValueAndDetectChanges(value, ref _scriptAssembly, Ps.Value.ControlAssemblySelector); } - } - /// /// Gets or set the path to the Python file in use /// @@ -303,17 +280,6 @@ namespace Umbraco.Core.Models set { SetPropertyValueAndDetectChanges(value, ref _scriptPath, Ps.Value.ScriptPathSelector); } } - /// - /// Gets or sets the path to the Xslt file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - public string XsltPath - { - get { return _xslt; } - set { SetPropertyValueAndDetectChanges(value, ref _xslt, Ps.Value.XsltPathSelector); } - } - /// /// Gets or sets a list of Macro Properties /// diff --git a/src/Umbraco.Core/Models/MacroTypes.cs b/src/Umbraco.Core/Models/MacroTypes.cs index 90eacf5171..310d6ccd7c 100644 --- a/src/Umbraco.Core/Models/MacroTypes.cs +++ b/src/Umbraco.Core/Models/MacroTypes.cs @@ -10,15 +10,11 @@ namespace Umbraco.Core.Models [DataContract(IsReference = true)] public enum MacroTypes { - [EnumMember] - Xslt = 1, [EnumMember] UserControl = 3, [EnumMember] Unknown = 4, [EnumMember] - Script = 6, - [EnumMember] PartialView = 7 } } diff --git a/src/Umbraco.Core/Persistence/Dtos/MacroDto.cs b/src/Umbraco.Core/Persistence/Dtos/MacroDto.cs index 821f092b38..2bfbcaba29 100644 --- a/src/Umbraco.Core/Persistence/Dtos/MacroDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/MacroDto.cs @@ -37,15 +37,7 @@ namespace Umbraco.Core.Persistence.Dtos [Column("macroScriptType")] [NullSetting(NullSetting = NullSettings.Null)] public string ScriptType { get; set; } - - [Column("macroScriptAssembly")] - [NullSetting(NullSetting = NullSettings.Null)] - public string ScriptAssembly { get; set; } - - [Column("macroXSLT")] - [NullSetting(NullSetting = NullSettings.Null)] - public string Xslt { get; set; } - + [Column("macroCacheByPage")] [Constraint(Default = "1")] public bool CacheByPage { get; set; } diff --git a/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs index a854a3a65f..a85de1fa57 100644 --- a/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Factories { public IMacro BuildEntity(MacroDto dto) { - var model = new Macro(dto.Id, dto.UniqueId, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.ScriptType, dto.ScriptAssembly, dto.Xslt, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.MacroFilePath); + var model = new Macro(dto.Id, dto.UniqueId, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.ScriptType, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.MacroFilePath); try { @@ -42,10 +42,8 @@ namespace Umbraco.Core.Persistence.Factories Name = entity.Name, MacroFilePath = entity.ScriptPath, RefreshRate = entity.CacheDuration, - ScriptAssembly = entity.ControlAssembly, ScriptType = entity.ControlType, UseInEditor = entity.UseInEditor, - Xslt = entity.XsltPath, MacroPropertyDtos = BuildPropertyDtos(entity) }; diff --git a/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs index c60feea9b8..07490a4cd8 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs @@ -18,14 +18,12 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Alias, dto => dto.Alias); CacheMap(src => src.CacheByPage, dto => dto.CacheByPage); CacheMap(src => src.CacheByMember, dto => dto.CachePersonalized); - CacheMap(src => src.ControlAssembly, dto => dto.ScriptAssembly); CacheMap(src => src.ControlType, dto => dto.ScriptType); CacheMap(src => src.DontRender, dto => dto.DontRender); CacheMap(src => src.Name, dto => dto.Name); CacheMap(src => src.CacheDuration, dto => dto.RefreshRate); CacheMap(src => src.ScriptPath, dto => dto.MacroFilePath); CacheMap(src => src.UseInEditor, dto => dto.UseInEditor); - CacheMap(src => src.XsltPath, dto => dto.Xslt); } } } diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index 72838c3d55..772d1f183d 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -296,9 +296,7 @@ namespace Umbraco.Core.Services xml.Add(new XElement("name", macro.Name)); xml.Add(new XElement("alias", macro.Alias)); xml.Add(new XElement("scriptType", macro.ControlType)); - xml.Add(new XElement("scriptAssembly", macro.ControlAssembly)); xml.Add(new XElement("scriptingFile", macro.ScriptPath)); - xml.Add(new XElement("xslt", macro.XsltPath)); xml.Add(new XElement("useInEditor", macro.UseInEditor.ToString())); xml.Add(new XElement("dontRender", macro.DontRender.ToString())); xml.Add(new XElement("refreshRate", macro.CacheDuration.ToString(CultureInfo.InvariantCulture))); diff --git a/src/Umbraco.Core/Services/Implement/MacroService.cs b/src/Umbraco.Core/Services/Implement/MacroService.cs index a28bb7ca50..1e0be6c4b3 100644 --- a/src/Umbraco.Core/Services/Implement/MacroService.cs +++ b/src/Umbraco.Core/Services/Implement/MacroService.cs @@ -31,9 +31,6 @@ namespace Umbraco.Core.Services.Implement /// internal static MacroTypes GetMacroType(IMacro macro) { - if (string.IsNullOrEmpty(macro.XsltPath) == false) - return MacroTypes.Xslt; - if (string.IsNullOrEmpty(macro.ScriptPath) == false) return MacroTypes.PartialView; diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index c83500c5d3..04e52833d6 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -1271,8 +1271,6 @@ namespace Umbraco.Core.Services.Implement var macroName = macroElement.Element("name").Value; var macroAlias = macroElement.Element("alias").Value; var controlType = macroElement.Element("scriptType").Value; - var controlAssembly = macroElement.Element("scriptAssembly").Value; - var xsltPath = macroElement.Element("xslt").Value; var scriptPath = macroElement.Element("scriptingFile").Value; //Following xml elements are treated as nullable properties @@ -1308,7 +1306,7 @@ namespace Umbraco.Core.Services.Implement } var existingMacro = _macroService.GetByAlias(macroAlias) as Macro; - var macro = existingMacro ?? new Macro(macroAlias, macroName, controlType, controlAssembly, xsltPath, scriptPath, + var macro = existingMacro ?? new Macro(macroAlias, macroName, controlType, scriptPath, cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); var properties = macroElement.Element("properties"); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index e9a781d560..bbb020f308 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -562,10 +562,6 @@ - - - - diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 29458deefb..c665fc366e 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -100,70 +100,6 @@ namespace Umbraco.Tests.Composing return new ProfilingLogger(logger, profiler); } - [Ignore("fixme - ignored test")] - [Test] - public void Benchmark_Original_Finder() - { - var profilingLogger = GetTestProfilingLogger(); - using (profilingLogger.TraceDuration("Starting test", "Finished test")) - { - using (profilingLogger.TraceDuration("Starting FindClassesOfType", "Finished FindClassesOfType")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinderOriginal.FindClassesOfType(_assemblies).Count(), 0); - } - } - using (profilingLogger.TraceDuration("Starting FindClassesOfTypeWithAttribute", "Finished FindClassesOfTypeWithAttribute")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinderOriginal.FindClassesOfTypeWithAttribute(_assemblies).Count(), 0); - } - } - using (profilingLogger.TraceDuration("Starting FindClassesWithAttribute", "Finished FindClassesWithAttribute")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinderOriginal.FindClassesWithAttribute(_assemblies).Count(), 0); - } - } - } - - } - - [Ignore("fixme - ignored test")] - [Test] - public void Benchmark_New_Finder() - { - var profilingLogger = GetTestProfilingLogger(); - using (profilingLogger.TraceDuration("Starting test", "Finished test")) - { - using (profilingLogger.TraceDuration("Starting FindClassesOfType", "Finished FindClassesOfType")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinder.FindClassesOfType(_assemblies).Count(), 0); - } - } - using (profilingLogger.TraceDuration("Starting FindClassesOfTypeWithAttribute", "Finished FindClassesOfTypeWithAttribute")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinder.FindClassesOfTypeWithAttribute(_assemblies).Count(), 0); - } - } - using (profilingLogger.TraceDuration("Starting FindClassesWithAttribute", "Finished FindClassesWithAttribute")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinder.FindClassesWithAttribute(_assemblies).Count(), 0); - } - } - } - - } - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class MyTestAttribute : Attribute { diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 6fba071709..4cdd9687f5 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -288,14 +288,7 @@ AnotherContentFinder var types = _typeLoader.GetDataEditors(); Assert.AreEqual(43, types.Count()); } - - [Test] - public void Resolves_XsltExtensions() - { - var types = _typeLoader.GetXsltExtensions(); - Assert.AreEqual(3, types.Count()); - } - + /// /// This demonstrates this issue: http://issues.umbraco.org/issue/U4-3505 - the TypeList was returning a list of assignable types /// not explicit types which is sort of ideal but is confusing so we'll do it the less confusing way. @@ -319,12 +312,6 @@ AnotherContentFinder Assert.IsNull(shouldNotFind); } - [XsltExtension("Blah.Blah")] - public class MyXsltExtension - { - - } - public interface IFindMe : IDiscoverable { diff --git a/src/Umbraco.Tests/Composing/XsltExtensionCollectionTests.cs b/src/Umbraco.Tests/Composing/XsltExtensionCollectionTests.cs deleted file mode 100644 index 399b1df7bb..0000000000 --- a/src/Umbraco.Tests/Composing/XsltExtensionCollectionTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Linq; -using LightInject; -using NUnit.Framework; -using Umbraco.Core.Macros; -using Umbraco.Web; - -namespace Umbraco.Tests.Composing -{ - [TestFixture] - public class XsltExtensionCollectionTests : ComposingTestBase - { - [Test] - public void XsltExtensionsCollectionBuilderWorks() - { - var container = new ServiceContainer(); - var builder = new XsltExtensionCollectionBuilder(container); - builder.AddExtensionObjectProducer(() => TypeLoader.GetXsltExtensions()); - var extensions = builder.CreateCollection(); - - Assert.AreEqual(3, extensions.Count()); - - Assert.IsTrue(extensions.Select(x => x.ExtensionObject.GetType()).Contains(typeof (XsltEx1))); - Assert.IsTrue(extensions.Select(x => x.ExtensionObject.GetType()).Contains(typeof(XsltEx2))); - Assert.AreEqual("test1", extensions.Single(x => x.ExtensionObject.GetType() == typeof(XsltEx1)).Namespace); - Assert.AreEqual("test2", extensions.Single(x => x.ExtensionObject.GetType() == typeof(XsltEx2)).Namespace); - } - - #region Test Objects - - [XsltExtension("test1")] - public class XsltEx1 - { } - - //test with legacy one - [umbraco.XsltExtension("test2")] - public class XsltEx2 - { } - - #endregion - } -} diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index e3a4db5390..98d36ca616 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -78,7 +78,6 @@ namespace Umbraco.Tests.Macros var model = new MacroModel { MacroType = macroType, - Xslt = "anything", ScriptName = "anything", TypeName = "anything" }; diff --git a/src/Umbraco.Tests/Models/MacroTests.cs b/src/Umbraco.Tests/Models/MacroTests.cs index 052c42942b..c1fda841c2 100644 --- a/src/Umbraco.Tests/Models/MacroTests.cs +++ b/src/Umbraco.Tests/Models/MacroTests.cs @@ -20,7 +20,7 @@ namespace Umbraco.Tests.Models [Test] public void Can_Deep_Clone() { - var macro = new Macro(1, Guid.NewGuid(), true, 3, "test", "Test", "blah", "blah", "xslt", false, true, true, "script"); + var macro = new Macro(1, Guid.NewGuid(), true, 3, "test", "Test", "blah", false, true, true, "script"); macro.Properties.Add(new MacroProperty(6, Guid.NewGuid(), "rewq", "REWQ", 1, "asdfasdf")); var clone = (Macro)macro.DeepClone(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs index de013426b7..d4dbd51991 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs @@ -37,7 +37,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var macro = new Macro("test1", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); + var macro = new Macro("test1", "Test", "~/usercontrol/blah.ascx", "~/views/macropartials/test.cshtml"); ; Assert.Throws(() => repository.Save(macro)); @@ -94,14 +94,12 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(macro.Alias, Is.EqualTo("test1")); Assert.That(macro.CacheByPage, Is.EqualTo(false)); Assert.That(macro.CacheByMember, Is.EqualTo(false)); - Assert.That(macro.ControlAssembly, Is.EqualTo("MyAssembly1")); Assert.That(macro.ControlType, Is.EqualTo("~/usercontrol/test1.ascx")); Assert.That(macro.DontRender, Is.EqualTo(true)); Assert.That(macro.Name, Is.EqualTo("Test1")); Assert.That(macro.CacheDuration, Is.EqualTo(0)); Assert.That(macro.ScriptPath, Is.EqualTo("~/views/macropartials/test1.cshtml")); Assert.That(macro.UseInEditor, Is.EqualTo(false)); - Assert.That(macro.XsltPath, Is.EqualTo("test1.xslt")); } @@ -171,7 +169,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); // Act - var macro = new Macro("test", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); + var macro = new Macro("test", "Test", "~/usercontrol/blah.ascx", "~/views/macropartials/test.cshtml"); macro.Properties.Add(new MacroProperty("test", "Test", 0, "test")); repository.Save(macro); @@ -197,12 +195,10 @@ namespace Umbraco.Tests.Persistence.Repositories macro.CacheDuration = 1234; macro.CacheByPage = true; macro.CacheByMember = true; - macro.ControlAssembly = ""; macro.ControlType = ""; macro.DontRender = false; macro.ScriptPath = "~/newpath.cshtml"; macro.UseInEditor = true; - macro.XsltPath = ""; repository.Save(macro); @@ -214,12 +210,10 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(macroUpdated.CacheDuration, Is.EqualTo(1234)); Assert.That(macroUpdated.CacheByPage, Is.EqualTo(true)); Assert.That(macroUpdated.CacheByMember, Is.EqualTo(true)); - Assert.That(macroUpdated.ControlAssembly, Is.EqualTo("")); Assert.That(macroUpdated.ControlType, Is.EqualTo("")); Assert.That(macroUpdated.DontRender, Is.EqualTo(false)); Assert.That(macroUpdated.ScriptPath, Is.EqualTo("~/newpath.cshtml")); Assert.That(macroUpdated.UseInEditor, Is.EqualTo(true)); - Assert.That(macroUpdated.XsltPath, Is.EqualTo("")); } } @@ -299,7 +293,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); + var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "~/views/macropartials/test1.cshtml"); macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); repository.Save(macro); @@ -324,7 +318,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); + var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "~/views/macropartials/test1.cshtml"); macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); repository.Save(macro); @@ -348,7 +342,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); + var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "~/views/macropartials/test1.cshtml"); var prop1 = new MacroProperty("blah1", "New1", 4, "test.editor"); var prop2 = new MacroProperty("blah2", "New2", 3, "test.editor"); @@ -434,9 +428,9 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - repository.Save(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml")); - repository.Save(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "MyAssembly2", "test2.xslt", "~/views/macropartials/test2.cshtml")); - repository.Save(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "MyAssembly3", "test3.xslt", "~/views/macropartials/test3.cshtml")); + repository.Save(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "~/views/macropartials/test1.cshtml")); + repository.Save(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "~/views/macropartials/test2.cshtml")); + repository.Save(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "~/views/macropartials/test3.cshtml")); scope.Complete(); } diff --git a/src/Umbraco.Tests/Services/MacroServiceTests.cs b/src/Umbraco.Tests/Services/MacroServiceTests.cs index c7e2978b19..44c5596bfc 100644 --- a/src/Umbraco.Tests/Services/MacroServiceTests.cs +++ b/src/Umbraco.Tests/Services/MacroServiceTests.cs @@ -28,9 +28,9 @@ namespace Umbraco.Tests.Services { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - repository.Save(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml")); - repository.Save(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "MyAssembly2", "test2.xslt", "~/views/macropartials/test2.cshtml")); - repository.Save(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "MyAssembly3", "test3.xslt", "~/views/macropartials/test3.cshtml")); + repository.Save(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "~/views/macropartials/test1.cshtml")); + repository.Save(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "~/views/macropartials/test2.cshtml")); + repository.Save(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "~/views/macropartials/test3.cshtml")); scope.Complete(); } } diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs index 17c065338d..36093e4feb 100644 --- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs +++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs @@ -23,7 +23,7 @@ namespace Umbraco.Tests.Services public void PackagingService_Can_Export_Macro() { // Arrange - var macro = new Macro("test1", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); + var macro = new Macro("test1", "Test", "~/usercontrol/blah.ascx", "~/views/macropartials/test.cshtml"); ServiceContext.MacroService.Save(macro); // Act diff --git a/src/Umbraco.Tests/UI/LegacyDialogTests.cs b/src/Umbraco.Tests/UI/LegacyDialogTests.cs index 5a978fb418..13503e4223 100644 --- a/src/Umbraco.Tests/UI/LegacyDialogTests.cs +++ b/src/Umbraco.Tests/UI/LegacyDialogTests.cs @@ -22,7 +22,6 @@ namespace Umbraco.Tests.UI } } - [TestCase(typeof(XsltTasks), Constants.Applications.Developer)] [TestCase(typeof(StylesheetTasks), Constants.Applications.Settings)] [TestCase(typeof(stylesheetPropertyTasks), Constants.Applications.Settings)] [TestCase(typeof(MemberGroupTasks), Constants.Applications.Members)] diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 0d964dcd21..3eb6d8ae73 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -335,7 +335,6 @@ - diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 787742de4e..451b5c5a32 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -567,7 +567,6 @@ - diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml index b1997a1c0a..c362f91df0 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml @@ -378,8 +378,6 @@ 讀取使用者控制項 %0% 錯誤 讀取使用者控制項 %0% 錯誤(組件:%0%,類別:%1%) 讀取巨集引擎腳本錯誤(檔案:%0%) - 分析XSLT檔案錯誤:%0% - 讀取XSLT檔案錯誤:%0% 請輸入標題 請選擇類型 圖片尺寸大於原始尺寸不會提高圖片品質,您確定要把圖片尺寸變大嗎? @@ -1350,4 +1348,4 @@ 轉址追蹤器已開啟。 啟動轉址追蹤器錯誤,更多資訊請參閱您的紀錄檔。 - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 993f845831..50ee041e9f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -434,8 +434,6 @@ Error loading userControl '%0%' Error loading customControl (Assembly: %0%, Type: '%1%') Error loading MacroEngine script (file: %0%) - "Error parsing XSLT file: %0% - "Error reading XSLT file: %0% Please enter a title Please choose a type You're about to make the picture larger than the original size. Are you sure that you want to proceed? 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 d3326c57b3..6304fda60f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -503,8 +503,6 @@ Error loading userControl '%0%' Error loading customControl (Assembly: %0%, Type: '%1%') Error loading MacroEngine script (file: %0%) - "Error parsing XSLT file: %0% - "Error reading XSLT file: %0% Please enter a title Please choose a type You're about to make the picture larger than the original size. Are you sure that you want to proceed? diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml index cf44e6e955..a259c11346 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml @@ -487,8 +487,6 @@ Error cargando userControl '%0%' Error cargandog customControl (Assembly: %0%, Type: '%1%') Error cargando MacroEngine script (file: %0%) - "Error analizando archivo XSLT: %0% - "Error leyendo archivo XSLT: %0% diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml index 37a3795d12..e047ff611b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml @@ -388,8 +388,6 @@ Erreur de chargement du userControl '%0%' Erreur de chargement d'un customControl (Assembly: %0%, Type: '%1%') Erreur de chargement d'un script du MacroEngine (fichier : %0%) - "Erreur de parsing d'un fichier XSLT : %0% - "Erreur de lecture d'un fichier XSLT : %0% Veuillez entrer un titre Veuillez choisir un type Vous allez définir une taille d'image supérieure à sa taille d'origine. Êtes-vous certain(e) de vouloir continuer? diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml index 36e294f1b8..1fcb8d889c 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml @@ -370,8 +370,6 @@ userControl の読み込みエラー '%0%' customControl の読み込みエラー (アセンブリ: %0%, タイプ: '%1%') MacroEngine スクリプトの読み込みエラー (ファイル: %0%) - XSLT ファイル解析エラー: %0% - XSLT ファイル読み込みエラー: %0% タイトルを入力してください 型を選択してください 元画像より大きくしようとしていますが、本当によろしいのですか? diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index d398a839ec..dd7f5b58a2 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -398,8 +398,6 @@ Error bij het laden van userControl '%0%' Error bij het laden van customControl (Assembly: %0%, Type: '%1%') Error bij het laden van MacroEngine script (file: %0%) - "Error bij het parsen van XSLT file: %0% - "Error bij het laden van XSLT file: %0% Vul een titel in Selecteer een type U wilt een afbeelding groter maken dan de originele afmetingen. Weet je zeker dat je wilt doorgaan? diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml index d47b06d10e..39e70003fe 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml @@ -487,8 +487,6 @@ Wystąpił błąd podczas ładowania userControl '%0%' Wystąpił błąd podczas ładowania customControl (Assembly: %0%, Typ: '%1%') Wystąpił błąd podczas ładowania skryptu MacroEngine (plik: %0%) - "Wystąpił błąd podczas parsowania pliku XSLT: %0% - "Wystąpił błąd odczytu pliku XSLT: %0% Proszę podać tytuł Proszę wybrać typ Chcesz utworzyć obraz większy niż rozmiar oryginalny. Czy na pewno chcesz kontynuować? diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index 009fbdb683..d234ab0e62 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -512,8 +512,6 @@ Ошибка загрузки пользовательского элемента управления '%0%' Ошибка загрузки внешнего типа (сборка: %0%, тип: '%1%') Ошибка загрузки макроса (файл: %0%) - "Ошибка разбора кода XSLT в файле: %0% - "Ошибка чтения XSLT-файла: %0% Ошибка в конфигурации типа данных, используемого для свойства, проверьте тип данных Укажите заголовок Выберите тип diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml index 8436976f9e..0439b09b3a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml @@ -314,8 +314,6 @@ Error loading userControl '%0%' Error loading customControl (Assembly: %0%, Type: '%1%') Error loading MacroEngine script (Dosya: %0%) - "Error parsing XSLT file: %0% - "Error reading XSLT file: %0% Lütfen bir başlık girin Lütfen bir tür seçin Orijinal boyutundan daha resmi büyütmek üzereyiz. Devam etmek istediğinizden emin misiniz? diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml index fed9f9d057..9513536c98 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml @@ -393,8 +393,6 @@ 加载 userControl 时出错 '%0%' 加载 customControl 时出错(程序集: %0%, 类型: '%1%') 加载 MacroEngine 脚本时出错 (文件: %0%) - "解析 xslt 文件时出错: %0% - "读取 xslt 文件时出错: %0% 请输入标题 请选择类型 图片尺寸大于原始尺寸不会提高图片质量,您确定要把图片尺寸变大吗? diff --git a/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.cs b/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.cs index d01d9b08e7..621b2926e6 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.cs +++ b/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.cs @@ -29,9 +29,9 @@ namespace Umbraco.Web.UI.Umbraco.Developer.Macros /// /// /// - protected override void PopulateFieldsOnLoad(IMacro macro, string macroAssemblyValue, string macroTypeValue) + protected override void PopulateFieldsOnLoad(IMacro macro, string macroTypeValue) { - base.PopulateFieldsOnLoad(macro, macroAssemblyValue, macroTypeValue); + base.PopulateFieldsOnLoad(macro, macroTypeValue); //check if the ScriptingFile property contains the MacroPartials path if (macro.ScriptPath.IsNullOrWhiteSpace() == false && (macro.ScriptPath.StartsWith(SystemDirectories.MvcViews + "/MacroPartials/") diff --git a/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.designer.cs b/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.designer.cs index a64101c102..a1febaa2ba 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.designer.cs +++ b/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.designer.cs @@ -3,15 +3,15 @@ // This code was generated by a tool. // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ namespace Umbraco.Web.UI.Umbraco.Developer.Macros { - - + + public partial class EditMacro { - + /// /// CssInclude1 control. /// @@ -20,7 +20,7 @@ namespace Umbraco.Web.UI.Umbraco.Developer.Macros { /// To modify move field declaration from designer file to code-behind file. /// protected global::ClientDependency.Core.Controls.CssInclude CssInclude1; - + /// /// SelectedPartialView control. /// @@ -29,7 +29,7 @@ namespace Umbraco.Web.UI.Umbraco.Developer.Macros { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.TextBox SelectedPartialView; - + /// /// PartialViewList control. /// diff --git a/src/Umbraco.Web.UI/umbraco/developer/Macros/assemblyBrowser.aspx b/src/Umbraco.Web.UI/umbraco/developer/Macros/assemblyBrowser.aspx deleted file mode 100644 index 4aaf155fa1..0000000000 --- a/src/Umbraco.Web.UI/umbraco/developer/Macros/assemblyBrowser.aspx +++ /dev/null @@ -1,28 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" Title="Assembly Browser" Codebehind="assemblyBrowser.aspx.cs" AutoEventWireup="True" - Inherits="umbraco.developer.assemblyBrowser" %> - - -

    - -

    The following list shows the Public Properties from the - Control. By checking the Properties and click the "Save Properties" button at - - the bottom, umbraco will create the corresponding Macro Elements.

    - -

    - -

    -
    - - -

    The following Macro Parameters was added:

    -
    -

    - Important: You might need to reload the macro to see the changes.

    - -

    - - -
    - -
    diff --git a/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx b/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx index d6291f0511..fc8aff082d 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx +++ b/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx @@ -68,27 +68,12 @@ - - - - - - - + - - - - - (Assembly)
    - - (Type) - -
    diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index d2a1ee6f80..e1317a902b 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -124,10 +124,7 @@ namespace Umbraco.Web.Composing internal static EditorValidatorCollection EditorValidators => Container.GetInstance(); - - internal static XsltExtensionCollection XsltExtensions - => Container.GetInstance(); - + internal static UmbracoApiControllerTypeCollection UmbracoApiControllerTypes => Container.GetInstance(); @@ -143,9 +140,6 @@ namespace Umbraco.Web.Composing internal static IPublishedSnapshotService PublishedSnapshotService => Container.GetInstance(); - public static ThumbnailProviderCollection ThumbnailProviders - => Container.GetInstance(); - #endregion #region Web Constants diff --git a/src/Umbraco.Web/CompositionExtensions.cs b/src/Umbraco.Web/CompositionExtensions.cs index 881a88c05f..e172af7b8a 100644 --- a/src/Umbraco.Web/CompositionExtensions.cs +++ b/src/Umbraco.Web/CompositionExtensions.cs @@ -33,15 +33,7 @@ namespace Umbraco.Core.Components /// internal static ActionCollectionBuilder Actions(this Composition composition) => composition.Container.GetInstance(); - - /// - /// Gets the content finders collection builder. - /// - /// The composition. - /// - internal static XsltExtensionCollectionBuilder XsltExtensions(this Composition composition) - => composition.Container.GetInstance(); - + /// /// Gets the content finders collection builder. /// @@ -80,13 +72,6 @@ namespace Umbraco.Core.Components internal static ImageUrlProviderCollectionBuilder ImageUrlProviders(this Composition composition) => composition.Container.GetInstance(); - /// - /// Gets the thumbnail providers collection builder. - /// - /// The composition. - internal static ThumbnailProviderCollectionBuilder ThumbnailProviders(this Composition composition) - => composition.Container.GetInstance(); - /// /// Gets the url providers collection builder. /// diff --git a/src/Umbraco.Web/Macros/MacroModel.cs b/src/Umbraco.Web/Macros/MacroModel.cs index 4239dbc827..28aad1b777 100644 --- a/src/Umbraco.Web/Macros/MacroModel.cs +++ b/src/Umbraco.Web/Macros/MacroModel.cs @@ -19,13 +19,8 @@ namespace Umbraco.Web.Macros public MacroTypes MacroType { get; set; } - // that one was for CustomControls which are gone in v8 - //public string TypeAssembly { get; set; } - public string TypeName { get; set; } - public string Xslt { get; set; } - public string ScriptName { get; set; } public string ScriptCode { get; set; } @@ -54,9 +49,7 @@ namespace Umbraco.Web.Macros Id = macro.Id; Name = macro.Name; Alias = macro.Alias; - //TypeAssembly = macro.ControlAssembly; TypeName = macro.ControlType; - Xslt = macro.XsltPath; ScriptName = macro.ScriptPath; CacheDuration = macro.CacheDuration; CacheByPage = macro.CacheByPage; diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index 9692b73b52..c1b613e2f2 100644 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -177,22 +177,12 @@ namespace Umbraco.Web.Macros switch (model.MacroType) { - case MacroTypes.Xslt: - filename = SystemDirectories.Xslt.EnsureEndsWith('/') + model.Xslt; - break; - //case MacroTypes.Script: - // // was "~/macroScripts/" - // filename = SystemDirectories.MacroScripts.EnsureEndsWith('/') + model.ScriptName; - // break; case MacroTypes.PartialView: filename = model.ScriptName; //partial views are saved with their full virtual path break; case MacroTypes.UserControl: filename = model.TypeName; //user controls are saved with their full virtual path break; - //case MacroTypes.Script: - //case MacroTypes.CustomControl: - //case MacroTypes.Unknown: default: // not file-based, or not supported filename = null; @@ -381,24 +371,7 @@ namespace Umbraco.Web.Macros "Executed PartialView.", () => ExecutePartialView(model), () => textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.ScriptName })); - - //case MacroTypes.Script: - // return ExecuteMacroWithErrorWrapper(model, - // "Executing Script: " + (string.IsNullOrWhiteSpace(model.ScriptCode) - // ? "ScriptName=\"" + model.ScriptName + "\"" - // : "Inline, Language=\"" + model.ScriptLanguage + "\""), - // "Executed Script.", - // () => ExecuteScript(model), - // () => textService.Localize("errors/macroErrorLoadingMacroEngineScript", new[] { model.ScriptName })); - - case MacroTypes.Xslt: - return ExecuteMacroWithErrorWrapper(model, - $"Executing Xslt: TypeName=\"{model.TypeName}\", ScriptName=\"{model.Xslt}\".", - "Executed Xslt.", - () => ExecuteXslt(model, _plogger), - // cannot diff. between reading & parsing... bah - () => textService.Localize("errors/macroErrorParsingXSLTFile", new[] { model.Xslt })); - + case MacroTypes.UserControl: return ExecuteMacroWithErrorWrapper(model, $"Loading UserControl: TypeName=\"{model.TypeName}\".", @@ -447,16 +420,6 @@ namespace Umbraco.Web.Macros return engine.Execute(macro, content); } - /// - /// Renders an Xslt Macro. - /// - /// The text output of the macro execution. - public static MacroContent ExecuteXslt(MacroModel macro, ProfilingLogger plogger) - { - var engine = new XsltMacroEngine(plogger); - return engine.Execute(macro); - } - public static MacroContent ExecuteUserControl(MacroModel macro) { // add tilde for v4 defined macros diff --git a/src/Umbraco.Web/Macros/XsltMacroEngine.cs b/src/Umbraco.Web/Macros/XsltMacroEngine.cs deleted file mode 100644 index 10fd6b5305..0000000000 --- a/src/Umbraco.Web/Macros/XsltMacroEngine.cs +++ /dev/null @@ -1,687 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Web; -using System.Web.Caching; -using System.Xml; -using System.Xml.XPath; -using System.Xml.Xsl; -using umbraco; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Xml.XPath; -using Umbraco.Web.Composing; -using Umbraco.Web.Templates; - -namespace Umbraco.Web.Macros -{ - /// - /// A macro engine using XSLT to execute. - /// - public class XsltMacroEngine - { - private readonly Func _getHttpContext; - private readonly Func _getUmbracoContext; - private readonly ProfilingLogger _plogger; - - public XsltMacroEngine(ProfilingLogger plogger) - { - _plogger = plogger; - - _getHttpContext = () => - { - if (HttpContext.Current == null) - throw new InvalidOperationException("The Xslt Macro Engine cannot execute with a null HttpContext.Current reference"); - return new HttpContextWrapper(HttpContext.Current); - }; - - _getUmbracoContext = () => - { - if (UmbracoContext.Current == null) - throw new InvalidOperationException("The Xslt Macro Engine cannot execute with a null UmbracoContext.Current reference"); - return UmbracoContext.Current; - }; - } - - #region Execute Xslt - - // executes the macro, relying on GetXsltTransform - // will pick XmlDocument or Navigator mode depending on the capabilities of the published caches - public MacroContent Execute(MacroModel model) - { - var hasCode = string.IsNullOrWhiteSpace(model.ScriptCode) == false; - var hasXslt = string.IsNullOrWhiteSpace(model.Xslt) == false; - - if (hasXslt == false && hasCode == false) - { - Current.Logger.Warn("Xslt is empty"); - return MacroContent.Empty; - } - - if (hasCode && model.ScriptLanguage.InvariantEquals("xslt") == false) - { - Current.Logger.Warn("Unsupported script language \"" + model.ScriptLanguage + "\"."); - return MacroContent.Empty; - } - - var msg = "Executing Xslt: " + (hasCode ? "Inline." : "Xslt=\"" + model.Xslt + "\"."); - using (_plogger.DebugDuration(msg, "ExecutedXslt.")) - { - // need these two here for error reporting - MacroNavigator macroNavigator = null; - XmlDocument macroXml = null; - - IXPathNavigable macroNavigable; - IXPathNavigable contentNavigable; - - var xmlCache = UmbracoContext.Current.ContentCache as PublishedCache.XmlPublishedCache.PublishedContentCache; - - if (xmlCache == null) - { - // a different cache - // inheriting from NavigableNavigator is required - - var contentNavigator = UmbracoContext.Current.ContentCache.CreateNavigator() as NavigableNavigator; - var mediaNavigator = UmbracoContext.Current.MediaCache.CreateNavigator() as NavigableNavigator; - - if (contentNavigator == null || mediaNavigator == null) - throw new Exception("Published caches XPathNavigator do not inherit from NavigableNavigator."); - - var parameters = new List(); - foreach (var prop in model.Properties) - AddMacroParameter(parameters, contentNavigator, mediaNavigator, prop.Key, prop.Type, prop.Value); - - macroNavigable = macroNavigator = new MacroNavigator(parameters); - contentNavigable = UmbracoContext.Current.ContentCache; - } - else - { - // the original XML cache - // render the macro on top of the Xml document - - var umbracoXml = xmlCache.GetXml(UmbracoContext.Current.InPreviewMode); - - macroXml = new XmlDocument(); - macroXml.LoadXml(""); - - foreach (var prop in model.Properties) - AddMacroXmlNode(umbracoXml, macroXml, prop.Key, prop.Type, prop.Value); - - macroNavigable = macroXml; - contentNavigable = umbracoXml; - } - - // this is somewhat ugly but eh... - var httpContext = _getHttpContext(); - if (httpContext.Request.QueryString["umbDebug"] != null && GlobalSettings.DebugMode) - { - var outerXml = macroXml?.OuterXml ?? macroNavigator.OuterXml; - var text = $"
    Debug from {model.Name}
    {HttpUtility.HtmlEncode(outerXml)}
    "; - return new MacroContent { Text = text }; - } - - // get the transform - XslCompiledTransform transform; - if (hasCode) - { - try - { - using (var sreader = new StringReader(model.ScriptCode)) - using (var xreader = new XmlTextReader(sreader)) - { - transform = GetXsltTransform(xreader, GlobalSettings.DebugMode); - } - } - catch (Exception e) - { - throw new Exception("Failed to parse inline Xslt.", e); - } - } - else - { - try - { - transform = GetCachedXsltTransform(model.Xslt); - } - catch (Exception e) - { - throw new Exception($"Failed to read Xslt file \"{model.Xslt}\".", e); - } - } - - using (_plogger.DebugDuration("Performing transformation.", "Performed transformation.")) - { - try - { - var transformed = XsltTransform(_plogger, macroNavigable, contentNavigable, transform); - var text = TemplateUtilities.ResolveUrlsFromTextString(transformed); - return new MacroContent { Text = text }; - } - catch (Exception e) - { - throw new Exception($"Failed to exec Xslt file \"{model.Xslt}\".", e); - } - } - } - } - - /// - /// Adds the XSLT extension namespaces to the XSLT header using - /// {0} as the container for the namespace references and - /// {1} as the container for the exclude-result-prefixes - /// - /// The XSLT - /// The XSLT with {0} and {1} replaced. - /// This is done here because it needs the engine's XSLT extensions. - public static string AddXsltExtensionsToHeader(string xslt) - { - var namespaceList = new StringBuilder(); - var namespaceDeclaractions = new StringBuilder(); - foreach (var extension in GetXsltExtensions()) - { - namespaceList.Append(extension.Key).Append(' '); - namespaceDeclaractions.AppendFormat("xmlns:{0}=\"urn:{0}\" ", extension.Key); - } - - // parse xslt - xslt = xslt.Replace("{0}", namespaceDeclaractions.ToString()); - xslt = xslt.Replace("{1}", namespaceList.ToString()); - return xslt; - } - - public static string TestXsltTransform(ProfilingLogger plogger, string xsltText, int currentPageId = -1) - { - IXPathNavigable macroNavigable; - IXPathNavigable contentNavigable; - - var xmlCache = UmbracoContext.Current.ContentCache as PublishedCache.XmlPublishedCache.PublishedContentCache; - - var xslParameters = new Dictionary(); - xslParameters["currentPage"] = UmbracoContext.Current.ContentCache - .CreateNavigator() - .Select(currentPageId > 0 ? ("//* [@id=" + currentPageId + "]") : "//* [@parentID=-1]"); - - if (xmlCache == null) - { - // a different cache - // inheriting from NavigableNavigator is required - - var contentNavigator = UmbracoContext.Current.ContentCache.CreateNavigator() as NavigableNavigator; - if (contentNavigator == null) - throw new Exception("Published caches XPathNavigator do not inherit from NavigableNavigator."); - - var parameters = new List(); - macroNavigable = new MacroNavigator(parameters); - contentNavigable = UmbracoContext.Current.ContentCache; - } - else - { - // the original XML cache - // render the macro on top of the Xml document - - var umbracoXml = xmlCache.GetXml(UmbracoContext.Current.InPreviewMode); - - var macroXml = new XmlDocument(); - macroXml.LoadXml(""); - - macroNavigable = macroXml; - contentNavigable = umbracoXml; - } - - // for a test, do not try...catch - // but let the exceptions be thrown - - XslCompiledTransform transform; - using (var reader = new XmlTextReader(new StringReader(xsltText))) - { - transform = GetXsltTransform(reader, true); - } - var transformed = XsltTransform(plogger, macroNavigable, contentNavigable, transform, xslParameters); - - return transformed; - } - - public static string ExecuteItemRenderer(ProfilingLogger plogger, XslCompiledTransform transform, string itemData) - { - IXPathNavigable macroNavigable; - IXPathNavigable contentNavigable; - - var xmlCache = UmbracoContext.Current.ContentCache as PublishedCache.XmlPublishedCache.PublishedContentCache; - - if (xmlCache == null) - { - // a different cache - // inheriting from NavigableNavigator is required - - var contentNavigator = UmbracoContext.Current.ContentCache.CreateNavigator() as NavigableNavigator; - var mediaNavigator = UmbracoContext.Current.MediaCache.CreateNavigator() as NavigableNavigator; - - if (contentNavigator == null || mediaNavigator == null) - throw new Exception("Published caches XPathNavigator do not inherit from NavigableNavigator."); - - var parameters = new List(); - - macroNavigable = new MacroNavigator(parameters); - contentNavigable = UmbracoContext.Current.ContentCache; - } - else - { - // the original XML cache - // render the macro on top of the Xml document - - var umbracoXml = xmlCache.GetXml(UmbracoContext.Current.InPreviewMode); - - var macroXml = new XmlDocument(); - macroXml.LoadXml(""); - - macroNavigable = macroXml; - contentNavigable = umbracoXml; - } - - var xslParameters = new Dictionary { { "itemData", itemData } }; - return XsltTransform(plogger, macroNavigable, contentNavigable, transform, xslParameters); - } - - #endregion - - #region XsltTransform - - // running on the XML cache, document mode - // add parameters to the root node - // note: contains legacy dirty code - private static void AddMacroXmlNode(XmlDocument umbracoXml, XmlDocument macroXml, - string macroPropertyAlias, string macroPropertyType, string macroPropertyValue) - { - var macroXmlNode = macroXml.CreateNode(XmlNodeType.Element, macroPropertyAlias, string.Empty); - - // if no value is passed, then use the current "pageID" as value - var contentId = macroPropertyValue == string.Empty ? UmbracoContext.Current.PageId.ToString() : macroPropertyValue; - - Current.Logger.Info($"Xslt node adding search start ({macroPropertyAlias},{macroPropertyValue})"); - - switch (macroPropertyType) - { - case "contentTree": - var nodeId = macroXml.CreateAttribute("nodeID"); - nodeId.Value = contentId; - macroXmlNode.Attributes.SetNamedItem(nodeId); - - // Get subs - try - { - macroXmlNode.AppendChild(macroXml.ImportNode(umbracoXml.GetElementById(contentId), true)); - } - catch - { } - break; - - case "contentCurrent": - var importNode = macroPropertyValue == string.Empty - ? umbracoXml.GetElementById(contentId) - : umbracoXml.GetElementById(macroPropertyValue); - - var currentNode = macroXml.ImportNode(importNode, true); - - // remove all sub content nodes - foreach (XmlNode n in currentNode.SelectNodes("*[@isDoc]")) - currentNode.RemoveChild(n); - - macroXmlNode.AppendChild(currentNode); - - break; - - case "contentSubs": // disable that one, it does not work anyway... - //x.LoadXml(""); - //x.FirstChild.AppendChild(x.ImportNode(umbracoXml.GetElementById(contentId), true)); - //macroXmlNode.InnerXml = TransformMacroXml(x, "macroGetSubs.xsl"); - break; - - case "contentAll": - macroXmlNode.AppendChild(macroXml.ImportNode(umbracoXml.DocumentElement, true)); - break; - - case "contentRandom": - XmlNode source = umbracoXml.GetElementById(contentId); - if (source != null) - { - var sourceList = source.SelectNodes("*[@isDoc]"); - if (sourceList.Count > 0) - { - int rndNumber; - var r = library.GetRandom(); - lock (r) - { - rndNumber = r.Next(sourceList.Count); - } - var node = macroXml.ImportNode(sourceList[rndNumber], true); - // remove all sub content nodes - foreach (XmlNode n in node.SelectNodes("*[@isDoc]")) - node.RemoveChild(n); - - macroXmlNode.AppendChild(node); - } - else - Current.Logger.Warn("Error adding random node - parent (" + macroPropertyValue + ") doesn't have children!"); - } - else - Current.Logger.Warn("Error adding random node - parent (" + macroPropertyValue + ") doesn't exists!"); - break; - - case "mediaCurrent": - if (string.IsNullOrEmpty(macroPropertyValue) == false) - { - //var c = new global::umbraco.cms.businesslogic.Content(int.Parse(macroPropertyValue)); - //macroXmlNode.AppendChild(macroXml.ImportNode(c.ToXml(global::umbraco.content.Instance.XmlContent, false), true)); - var nav = UmbracoContext.Current.MediaCache.CreateNodeNavigator(int.Parse(macroPropertyValue), false); - if (nav != null) - macroXmlNode.AppendChild(macroXml.ReadNode(nav.ReadSubtree())); - } - break; - - default: - macroXmlNode.InnerText = HttpUtility.HtmlDecode(macroPropertyValue); - break; - } - macroXml.FirstChild.AppendChild(macroXmlNode); - } - - // running on a navigable cache, navigable mode - // add parameters to the macro parameters collection - private static void AddMacroParameter(ICollection parameters, - NavigableNavigator contentNavigator, NavigableNavigator mediaNavigator, - string macroPropertyAlias, string macroPropertyType, string macroPropertyValue) - { - // if no value is passed, then use the current "pageID" as value - var contentId = macroPropertyValue == string.Empty ? UmbracoContext.Current.PageId.ToString() : macroPropertyValue; - - Current.Logger.Info($"Xslt node adding search start ({macroPropertyAlias},{macroPropertyValue})"); - - // beware! do not use the raw content- or media- navigators, but clones !! - - switch (macroPropertyType) - { - case "contentTree": - parameters.Add(new MacroNavigator.MacroParameter( - macroPropertyAlias, - contentNavigator.CloneWithNewRoot(contentId), // null if not found - will be reported as empty - attributes: new Dictionary { { "nodeID", contentId } })); - - break; - - case "contentPicker": - parameters.Add(new MacroNavigator.MacroParameter( - macroPropertyAlias, - contentNavigator.CloneWithNewRoot(contentId), // null if not found - will be reported as empty - 0)); - break; - - case "contentSubs": - parameters.Add(new MacroNavigator.MacroParameter( - macroPropertyAlias, - contentNavigator.CloneWithNewRoot(contentId), // null if not found - will be reported as empty - 1)); - break; - - case "contentAll": - parameters.Add(new MacroNavigator.MacroParameter(macroPropertyAlias, contentNavigator.Clone())); - break; - - case "contentRandom": - var nav = contentNavigator.Clone(); - if (nav.MoveToId(contentId)) - { - var descendantIterator = nav.Select("./* [@isDoc]"); - if (descendantIterator.MoveNext()) - { - // not empty - and won't change - var descendantCount = descendantIterator.Count; - - int index; - var r = library.GetRandom(); - lock (r) - { - index = r.Next(descendantCount); - } - - while (index > 0 && descendantIterator.MoveNext()) - index--; - - var node = descendantIterator.Current.UnderlyingObject as INavigableContent; - if (node != null) - { - nav = contentNavigator.CloneWithNewRoot(node.Id); - parameters.Add(new MacroNavigator.MacroParameter(macroPropertyAlias, nav, 0)); - } - else - throw new InvalidOperationException("Iterator contains non-INavigableContent elements."); - } - else - Current.Logger.Warn("Error adding random node - parent (" + macroPropertyValue + ") doesn't have children!"); - } - else - Current.Logger.Warn("Error adding random node - parent (" + macroPropertyValue + ") doesn't exists!"); - break; - - case "mediaCurrent": - parameters.Add(new MacroNavigator.MacroParameter( - macroPropertyAlias, - mediaNavigator.CloneWithNewRoot(contentId), // null if not found - will be reported as empty - 0)); - break; - - default: - parameters.Add(new MacroNavigator.MacroParameter(macroPropertyAlias, HttpUtility.HtmlDecode(macroPropertyValue))); - break; - } - } - - // gets the result of the xslt transform - private static string XsltTransform(ProfilingLogger plogger, IXPathNavigable macroNavigable, IXPathNavigable contentNavigable, - XslCompiledTransform xslt, IDictionary xslParameters = null) - { - TextWriter tw = new StringWriter(); - - XsltArgumentList xslArgs; - using (plogger.DebugDuration("Adding Xslt extensions", "Added Xslt extensions")) - { - xslArgs = GetXsltArgumentListWithExtensions(); - var lib = new library(); - xslArgs.AddExtensionObject("urn:umbraco.library", lib); - } - - // add parameters - if (xslParameters == null || xslParameters.ContainsKey("currentPage") == false) - { - // note: "PageId" is a legacy stuff that might be != from what's in current PublishedContentRequest - var currentPageId = UmbracoContext.Current.PageId; - var current = contentNavigable.CreateNavigator().Select("//* [@id=" + currentPageId + "]"); - xslArgs.AddParam("currentPage", string.Empty, current); - } - if (xslParameters != null) - foreach (var parameter in xslParameters) - xslArgs.AddParam(parameter.Key, string.Empty, parameter.Value); - - // transform - using (plogger.DebugDuration("Executing Xslt transform", "Executed Xslt transform")) - { - xslt.Transform(macroNavigable, xslArgs, tw); - } - - return tw.ToString(); - } - - #endregion - - #region Manage transforms - - private static XslCompiledTransform GetCachedXsltTransform(string filename) - { - //TODO: SD: Do we really need to cache this?? - var filepath = IOHelper.MapPath(SystemDirectories.Xslt.EnsureEndsWith('/') + filename); - return Current.ApplicationCache.GetCacheItem( - CacheKeys.MacroXsltCacheKey + filename, - CacheItemPriority.Default, - new CacheDependency(filepath), - () => - { - using (var xslReader = new XmlTextReader(filepath)) - { - return GetXsltTransform(xslReader, GlobalSettings.DebugMode); - } - }); - } - - public static XslCompiledTransform GetXsltTransform(XmlTextReader xslReader, bool debugMode) - { - var transform = new XslCompiledTransform(debugMode); - var xslResolver = new XmlUrlResolver - { - Credentials = CredentialCache.DefaultCredentials - }; - - xslReader.EntityHandling = EntityHandling.ExpandEntities; - xslReader.DtdProcessing = DtdProcessing.Parse; - - try - { - transform.Load(xslReader, XsltSettings.TrustedXslt, xslResolver); - } - finally - { - xslReader.Close(); - } - - return transform; - } - - #endregion - - #region Manage extensions - - /* - private static readonly string XsltExtensionsConfig = - IOHelper.MapPath(SystemDirectories.Config + "/xsltExtensions.config"); - - private static readonly Func XsltExtensionsDependency = - () => new CacheDependency(XsltExtensionsConfig); - */ - - // creates and return an Xslt argument list with all Xslt extensions. - public static XsltArgumentList GetXsltArgumentListWithExtensions() - { - var xslArgs = new XsltArgumentList(); - - foreach (var extension in GetXsltExtensions()) - { - var extensionNamespace = "urn:" + extension.Key; - xslArgs.AddExtensionObject(extensionNamespace, extension.Value); - Current.Logger.Info($"Extension added: {extensionNamespace}, {extension.Value.GetType().Name}"); - } - - return xslArgs; - } - - /* - // gets the collection of all XSLT extensions for macros - // ie predefined, configured in the config file, and marked with the attribute - public static Dictionary GetCachedXsltExtensions() - { - // We could cache the extensions in a static variable but then the cache - // would not be refreshed when the .config file is modified. An application - // restart would be required. Better use the cache and add a dependency. - - // SD: The only reason the above statement might be true is because the xslt extension .config file is not a - // real config file!! if it was, we wouldn't have this issue. Having these in a static variable would be preferred! - // If you modify a config file, the app restarts and thus all static variables are reset. - // Having this stuff in cache just adds to the gigantic amount of cache data and will cause more cache turnover to happen. - - return ApplicationContext.Current.ApplicationCache.GetCacheItem( - "UmbracoXsltExtensions", - CacheItemPriority.NotRemovable, // NH 4.7.1, Changing to NotRemovable - null, // no refresh action - XsltExtensionsDependency(), // depends on the .config file - TimeSpan.FromDays(1), // expires in 1 day (?) - GetXsltExtensions); - } - */ - - // actually gets the collection of all XSLT extensions for macros - // ie predefined, configured in the config file, and marked with the attribute - public static Dictionary GetXsltExtensions() - { - return Current.XsltExtensions - .ToDictionary(x => x.Namespace, x => x.ExtensionObject); - - /* - // initialize the collection - // there is no "predefined" extensions anymore - var extensions = new Dictionary(); - - // Load the XSLT extensions configuration - var xsltExt = new XmlDocument(); - xsltExt.Load(XsltExtensionsConfig); - - // get the configured types - var extensionsNode = xsltExt.SelectSingleNode("/XsltExtensions"); - if (extensionsNode != null) - foreach (var attributes in extensionsNode.Cast() - .Where(x => x.NodeType == XmlNodeType.Element) - .Select(x => x.Attributes)) - { - Debug.Assert(attributes["assembly"] != null, "Extension attribute 'assembly' not specified."); - Debug.Assert(attributes["type"] != null, "Extension attribute 'type' not specified."); - Debug.Assert(attributes["alias"] != null, "Extension attribute 'alias' not specified."); - - // load the extension assembly - var extensionFile = IOHelper.MapPath(string.Format("{0}/{1}.dll", - SystemDirectories.Bin, attributes["assembly"].Value)); - - Assembly extensionAssembly; - try - { - extensionAssembly = Assembly.LoadFrom(extensionFile); - } - catch (Exception ex) - { - throw new Exception( - String.Format("Could not load assembly {0} for XSLT extension {1}. Please check config/xsltExtensions.config.", - extensionFile, attributes["alias"].Value), ex); - } - - // load the extension type - var extensionType = extensionAssembly.GetType(attributes["type"].Value); - if (extensionType == null) - throw new Exception( - String.Format("Could not load type {0} ({1}) for XSLT extension {2}. Please check config/xsltExtensions.config.", - attributes["type"].Value, extensionFile, attributes["alias"].Value)); - - // create an instance and add it to the extensions list - extensions.Add(attributes["alias"].Value, Activator.CreateInstance(extensionType)); - } - - // get types marked with XsltExtension attribute - var foundExtensions = TypeLoader.Current.ResolveXsltExtensions(); - foreach (var xsltType in foundExtensions) - { - var attributes = xsltType.GetCustomAttributes(true); - var xsltTypeName = xsltType.FullName; - foreach (var ns in attributes - .Select(attribute => string.IsNullOrEmpty(attribute.Namespace) ? attribute.Namespace : xsltTypeName)) - { - extensions.Add(ns, Activator.CreateInstance(xsltType)); - } - } - - return extensions; - */ - } - - #endregion - } -} diff --git a/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderCollection.cs b/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderCollection.cs deleted file mode 100644 index 1668677649..0000000000 --- a/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderCollection.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Composing; -using Umbraco.Core.Media; - -namespace Umbraco.Web.Media.ThumbnailProviders -{ - // fixme - kill entirely in v8 thumbs should be generated by ImageProcessor - public class ThumbnailProviderCollection : BuilderCollectionBase - { - public ThumbnailProviderCollection(IEnumerable items) - : base(items) - { } - - public string GetThumbnailUrl(string fileUrl) - { - var provider = this.FirstOrDefault(x => x.CanProvideThumbnail(fileUrl)); - return provider != null ? provider.GetThumbnailUrl(fileUrl) : string.Empty; - } - } -} diff --git a/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderCollectionBuilder.cs b/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderCollectionBuilder.cs deleted file mode 100644 index e5925ac4e7..0000000000 --- a/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderCollectionBuilder.cs +++ /dev/null @@ -1,15 +0,0 @@ -using LightInject; -using Umbraco.Core.Composing; -using Umbraco.Core.Media; - -namespace Umbraco.Web.Media.ThumbnailProviders -{ - public class ThumbnailProviderCollectionBuilder : WeightedCollectionBuilderBase - { - public ThumbnailProviderCollectionBuilder(IServiceContainer container) - : base(container) - { } - - protected override ThumbnailProviderCollectionBuilder This => this; - } -} diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 54afd0ad75..e408a46db5 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -121,9 +121,6 @@ namespace Umbraco.Web.Runtime composition.Container.EnableWebApi(GlobalConfiguration.Configuration); composition.Container.RegisterApiControllers(typeLoader, GetType().Assembly); - XsltExtensionCollectionBuilder.Register(composition.Container) - .AddExtensionObjectProducer(() => typeLoader.GetXsltExtensions()); - composition.Container.RegisterCollectionBuilder() .Add(() => typeLoader.GetTypes()); // fixme which searchable trees?! @@ -179,13 +176,7 @@ namespace Umbraco.Web.Runtime .Append(); composition.Container.RegisterSingleton(); - - composition.Container.RegisterCollectionBuilder() - .Add(typeLoader.GetThumbnailProviders()); - - composition.Container.RegisterCollectionBuilder() - .Append(typeLoader.GetImageUrlProviders()); - + composition.Container.RegisterSingleton(); // register *all* checks, except those marked [HideFromTypeFinder] of course diff --git a/src/Umbraco.Web/TypeLoaderExtensions.cs b/src/Umbraco.Web/TypeLoaderExtensions.cs index 2c7bc430ce..a1209abccf 100644 --- a/src/Umbraco.Web/TypeLoaderExtensions.cs +++ b/src/Umbraco.Web/TypeLoaderExtensions.cs @@ -63,36 +63,6 @@ namespace Umbraco.Web { return mgr.GetTypes(); } - - /// - /// Returns all classes attributed with XsltExtensionAttribute attribute - /// - /// - /// - internal static IEnumerable GetXsltExtensions(this TypeLoader mgr) - { - return mgr.GetAttributedTypes(); - } - - /// - /// Returns all IThumbnailProvider classes - /// - /// - /// - internal static IEnumerable GetThumbnailProviders(this TypeLoader mgr) - { - return mgr.GetTypes(); - } - - /// - /// Returns all IImageUrlProvider classes - /// - /// - /// - internal static IEnumerable GetImageUrlProviders(this TypeLoader mgr) - { - return mgr.GetTypes(); - } - + } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 9b771f09c9..5b4da825a8 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -204,7 +204,6 @@ - @@ -217,8 +216,6 @@ - - @@ -443,6 +440,7 @@ ASPXCodeBehind + @@ -808,9 +806,6 @@ ASPXCodeBehind - - ASPXCodeBehind - @@ -1181,9 +1176,6 @@ ASPXCodeBehind - - ASPXCodeBehind - ASPXCodeBehind @@ -1200,7 +1192,6 @@ ASPXCodeBehind - @@ -1293,7 +1284,6 @@ - FeedProxy.aspx ASPXCodeBehind @@ -1327,13 +1317,6 @@ Preview.aspx - - xsltVisualize.aspx - ASPXCodeBehind - - - xsltVisualize.aspx - insertMasterpageContent.aspx ASPXCodeBehind @@ -1374,13 +1357,6 @@ Code - - assemblyBrowser.aspx - ASPXCodeBehind - - - assemblyBrowser.aspx - editPackage.aspx ASPXCodeBehind @@ -1388,24 +1364,6 @@ editPackage.aspx - - getXsltStatus.asmx - Component - - - xsltChooseExtension.aspx - ASPXCodeBehind - - - xsltChooseExtension.aspx - - - xsltInsertValueOf.aspx - ASPXCodeBehind - - - xsltInsertValueOf.aspx - exportDocumenttype.aspx ASPXCodeBehind @@ -1466,11 +1424,6 @@ - - True - True - Resources.resx - default.aspx ASPXCodeBehind @@ -1506,9 +1459,6 @@ - - XmlTree.xsd - @@ -1519,10 +1469,6 @@ CheckForUpgrade.asmx Component - - codeEditorSave.asmx - Component - legacyAjaxCalls.asmx Component @@ -1571,11 +1517,6 @@ ResXFileCodeGenerator Strings.Designer.cs - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - @@ -1593,10 +1534,6 @@
    - - - - ASPXCodeBehind @@ -1604,7 +1541,6 @@ Form - @@ -1624,7 +1560,6 @@ ASPXCodeBehind - ASPXCodeBehind @@ -1655,11 +1590,6 @@ - - - - XmlTree.xsd - @@ -1685,7 +1615,6 @@ SettingsSingleFileGenerator Settings1.Designer.cs - diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs index d887a706d5..ced330c638 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs @@ -132,9 +132,6 @@ namespace umbraco.cms.businesslogic.packager if (appendFile) { - if (!string.IsNullOrEmpty(mcr.XsltPath)) - AppendFileToManifest(IOHelper.ResolveUrl(SystemDirectories.Xslt) + "/" + mcr.XsltPath, packageDirectory, doc); - //TODO: Clearly the packager hasn't worked very well for packaging Partial Views to date since there is no logic in here for that //if (!string.IsNullOrEmpty(mcr.ScriptingFile)) diff --git a/src/Umbraco.Web/umbraco.presentation/XsltExtensionAttribute.cs b/src/Umbraco.Web/umbraco.presentation/XsltExtensionAttribute.cs deleted file mode 100644 index b3e0e7dce8..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/XsltExtensionAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Security.Permissions; -using System.Web; - -namespace umbraco -{ - /// - /// Allows App_Code XSLT extensions to be declared using the [XsltExtension] class attribute. - /// - /// - /// An optional XML namespace can be specified using [XsltExtension("MyNamespace")]. - /// - [AttributeUsage(AttributeTargets.Class)] - [Obsolete("Use Umbraco.Core.Macros.XsltExtensionAttribute instead")] - public class XsltExtensionAttribute : Umbraco.Core.Macros.XsltExtensionAttribute - { - public XsltExtensionAttribute() : base() - { - - } - - public XsltExtensionAttribute(string ns) : base(ns) - { - - } - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/Trees.cd b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/Trees.cd deleted file mode 100644 index 28c6272d99..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/Trees.cd +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - umbraco\Trees\BaseTree.cs - AABUAABAAAQSAQAIAAAAQAAABRBgBEUBWDIAAMKAAyI= - - - - - - - umbraco\Trees\BaseContentTree.cs - ACAAAAAIBICAgAIIEAEAAAAAAAAAAAAAgBQAAAAAAAI= - - - - - - umbraco\Trees\loadContent.cs - AAAAAAAAAQAABAAIAAQAAAAAAABAAAABABAAAEAAAAA= - - - - - - umbraco\Trees\loadMedia.cs - AAAAAAAAAAAAAAAIAAAAAAAAAABAAAABABAAAEAAAAI= - - - - - - umbraco\Trees\FileSystemTree.cs - AAABAAAAAAAAABAAAAAAAAAAAAAAEAABABBAAAAAAAI= - - - - - - umbraco\Trees\loadXslt.cs - AAAAAAAAAAAAABAAAAAAAAAAAAAAEAABAABAAAAAAAI= - - - - - - umbraco\Trees\loadPython.cs - AAAAAAAAAAAAABAAAAAAAAAAAAAAEAABAABAAAAAAAI= - - - - - - umbraco\Trees\ContentRecycleBin.cs - AAAAAAAAAAAAAAAIEAAAAAAAAABAAAABAAIAAEAAAAA= - - - - - - umbraco\Trees\LegacyTree.cs - AAAAAAAAACAAAAAAAAAAAAABACAAAAABABAAAAAAAAI= - - - - - - umbraco\Trees\NullTree.cs - AAAAAAAAAAAAAAAAAAAAAAAAAQAAAAABABAAAAAAAAI= - - - - - - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/XmlTree.xsd b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/XmlTree.xsd deleted file mode 100644 index e577da3663..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/XmlTree.xsd +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/XmlTree.xsx b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/XmlTree.xsx deleted file mode 100644 index ff71343be3..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/XmlTree.xsx +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/XsltTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/XsltTasks.cs deleted file mode 100644 index 8b9f5cb8e5..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/XsltTasks.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.IO; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Web.UI; -using Umbraco.Core.FileResources; -using Umbraco.Core.Models; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web._Legacy.UI; -using File = System.IO.File; - -namespace umbraco -{ - /// - /// Summary description for standardTasks. - /// - /// - - public class XsltTasks : LegacyDialogTask - { - - public override bool PerformSave() - { - IOHelper.EnsurePathExists(SystemDirectories.Xslt); - IOHelper.EnsureFileExists(Path.Combine(IOHelper.MapPath(SystemDirectories.Xslt), "web.config"), Files.BlockingWebConfig); - - var template = Alias.Substring(0, Alias.IndexOf("|||")); - var fileName = Alias.Substring(Alias.IndexOf("|||") + 3, Alias.Length - Alias.IndexOf("|||") - 3).Replace(" ", ""); - if (fileName.ToLowerInvariant().EndsWith(".xslt") == false) - fileName += ".xslt"; - var xsltTemplateSource = IOHelper.MapPath(SystemDirectories.Umbraco + "/xslt/templates/" + template); - var xsltNewFilename = IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName); - - if (File.Exists(xsltNewFilename) == false) - { - if (fileName.Contains("/")) //if there's a / create the folder structure for it - { - var folders = fileName.Split("/".ToCharArray()); - var xsltBasePath = IOHelper.MapPath(SystemDirectories.Xslt); - for (var i = 0; i < folders.Length - 1; i++) - { - xsltBasePath = System.IO.Path.Combine(xsltBasePath, folders[i]); - System.IO.Directory.CreateDirectory(xsltBasePath); - } - } - - // update with xslt references - var xslt = ""; - using (var xsltFile = System.IO.File.OpenText(xsltTemplateSource)) - { - xslt = xsltFile.ReadToEnd(); - xsltFile.Close(); - } - - // prepare support for XSLT extensions - xslt = Umbraco.Web.Macros.XsltMacroEngine.AddXsltExtensionsToHeader(xslt); - var xsltWriter = System.IO.File.CreateText(xsltNewFilename); - xsltWriter.Write(xslt); - xsltWriter.Flush(); - xsltWriter.Close(); - - // Create macro? - if (ParentID == 1) - { - var name = Alias.Substring(Alias.IndexOf("|||") + 3, Alias.Length - Alias.IndexOf("|||") - 3); - if (name.ToLowerInvariant().EndsWith(".xslt")) - name = name.Substring(0, name.Length - 5); - - name = name.SplitPascalCasing().ToFirstUpperInvariant(); - //cms.businesslogic.macro.Macro m = - // cms.businesslogic.macro.Macro.MakeNew(name); - var m = new Macro - { - Name = name, - Alias = name.Replace(" ", String.Empty) - }; - m.XsltPath = fileName; - //m.Save(); - Current.Services.MacroService.Save(m); - } - } - - _returnUrl = string.Format(SystemDirectories.Umbraco + "/developer/xslt/editXslt.aspx?file={0}", fileName); - - return true; - } - - public override bool PerformDelete() - { - var path = IOHelper.MapPath(SystemDirectories.Xslt + "/" + Alias.TrimStart('/')); - - try - { - if(System.IO.Directory.Exists(path)) - System.IO.Directory.Delete(path); - else if(System.IO.File.Exists(path)) - System.IO.File.Delete(path); - } - catch (Exception ex) - { - Current.Logger.Error(string.Format("Could not remove XSLT file {0} - User {1}", Alias, UmbracoContext.Current.Security.GetUserId()), ex); - } - return true; - } - - private string _returnUrl = ""; - - public override string ReturnUrl - { - get { return _returnUrl; } - } - - public override string AssignedApp - { - get { return Constants.Applications.Developer.ToString(); } - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/xslt.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/xslt.ascx.cs deleted file mode 100644 index 90ffd2101e..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/xslt.ascx.cs +++ /dev/null @@ -1,127 +0,0 @@ -using Umbraco.Core.Services; -using System.Web; -using System.Web.UI.WebControls; -using System.IO; -using Umbraco.Core; -using Umbraco.Web.UI; -using Umbraco.Core.IO; -using Umbraco.Web; -using Umbraco.Web.UI.Controls; -using Umbraco.Web._Legacy.UI; - -namespace umbraco.presentation.create -{ - - - /// - /// Summary description for xslt. - /// - public partial class xslt : UmbracoUserControl - { - protected System.Web.UI.WebControls.ListBox nodeType; - - protected void Page_Load(object sender, System.EventArgs e) - { - sbmt.Text = Services.TextService.Localize("create"); - foreach (string fileName in Directory.GetFiles(IOHelper.MapPath(SystemDirectories.Umbraco + GetXsltTemplatePath()), "*.xslt")) - { - FileInfo fi = new FileInfo(fileName); - if (fi.Name != "Clean.xslt") - { - var liText = fi.Name.Replace(".xslt", "").SplitPascalCasing().ToFirstUpperInvariant(); - xsltTemplate.Items.Add(new ListItem(liText, fi.Name)); - } - } - - } - - private static string GetXsltTemplatePath() - { - return "/xslt/templates/schema2"; - } - - protected void sbmt_Click(object sender, System.EventArgs e) - { - if (Page.IsValid) - { - var createMacroVal = 0; - if (createMacro.Checked) - createMacroVal = 1; - - var xsltName = Path.Combine("schema2", xsltTemplate.SelectedValue); - - - var returnUrl = LegacyDialogHandler.Create( - new HttpContextWrapper(Context), - Security.CurrentUser, - Request.GetItemAsString("nodeType"), - createMacroVal, - xsltName + "|||" + rename.Text); - - ClientTools - .ChangeContentFrameUrl(returnUrl) - .ChildNodeCreated() - .CloseModalWindow(); - - - - - } - - } - - /// - /// rename control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox rename; - - /// - /// RequiredFieldValidator1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; - - /// - /// xsltTemplate control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.ListBox xsltTemplate; - - /// - /// createMacro control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox createMacro; - - /// - /// Textbox1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox Textbox1; - - /// - /// sbmt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button sbmt; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx deleted file mode 100644 index 18b582707d..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx +++ /dev/null @@ -1,29 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoDialog.Master" Title="Assembly Browser" Codebehind="assemblyBrowser.aspx.cs" AutoEventWireup="True" - Inherits="umbraco.developer.assemblyBrowser" %> - - -

    - - -

    The following list shows the Public Properties from the - Control. By checking the Properties and click the "Save Properties" button at - - the bottom, umbraco will create the corresponding Macro Elements.

    - -

    - -

    -
    - - -

    The following Macro Parameters was added:

    -
    -

    - Important: You might need to reload the macro to see the changes.

    - -

    - - -
    - -
    diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.cs deleted file mode 100644 index 96b815019f..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.cs +++ /dev/null @@ -1,204 +0,0 @@ -using System; -using System.Linq; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Reflection; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Core.Models; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.UI.Pages; -using UserControl = System.Web.UI.UserControl; - -namespace umbraco.developer -{ - /// - /// Summary description for assemblyBrowser. - /// - public partial class assemblyBrowser : UmbracoEnsuredPage - { - public assemblyBrowser() - { - CurrentApp = Constants.Applications.Developer.ToString(); - } - protected void Page_Load(object sender, EventArgs e) - { - - var isUserControl = false; - var errorReadingControl = false; - - try - { - - Type type = null; - if (Request.QueryString["type"] == null) - { - isUserControl = true; - var fileName = Request.GetItemAsString("fileName"); - if (!fileName.StartsWith("~")) - { - if (fileName.StartsWith("/")) - { - fileName = "~" + fileName; - } - else - { - fileName = "~/" + fileName; - } - } - IOHelper.ValidateEditPath(fileName, SystemDirectories.UserControls); - - if (System.IO.File.Exists(IOHelper.MapPath(fileName))) - { - var oControl = (UserControl)LoadControl(fileName); - - type = oControl.GetType(); - } - else - { - errorReadingControl = true; - ChooseProperties.Visible = false; - AssemblyName.Text = "User control doesn't exist

    Please verify that you've copied the file to:
    " + IOHelper.MapPath("~/" + fileName); - } - } - else - { - var currentAss = IOHelper.MapPath(SystemDirectories.Bin + "/" + Request.QueryString["fileName"] + ".dll"); - var asm = Assembly.LoadFrom(currentAss); - type = asm.GetType(Request.QueryString["type"]); - } - - if (!errorReadingControl) - { - string fullControlAssemblyName; - if (isUserControl) - { - AssemblyName.Text = "Choose Properties from " + type.BaseType.Name; - fullControlAssemblyName = type.BaseType.Namespace + "." + type.BaseType.Name; - } - else - { - AssemblyName.Text = "Choose Properties from " + type.Name; - fullControlAssemblyName = type.Namespace + "." + type.Name; - } - - - if (!IsPostBack && type != null) - { - MacroProperties.Items.Clear(); - foreach (var pi in type.GetProperties()) - { - if (pi.CanWrite && ((fullControlAssemblyName == pi.DeclaringType.Namespace + "." + pi.DeclaringType.Name) || pi.DeclaringType == type)) - { - MacroProperties.Items.Add(new ListItem(pi.Name + " (" + pi.PropertyType.Name + ")", pi.PropertyType.Name)); - } - - foreach (ListItem li in MacroProperties.Items) - li.Selected = true; - } - } - - } - } - catch (Exception err) - { - AssemblyName.Text = "Error reading " + Request.CleanForXss("fileName"); - Button1.Visible = false; - ChooseProperties.Controls.Add(new LiteralControl("

    " + err.ToString() + "

    ")); - } - - } - - protected void Button1_Click(object sender, EventArgs e) - { - var result = ""; - - // Get the macro object - var macroObject = Current.Services.MacroService.GetById(Convert.ToInt32(Request.QueryString["macroID"])); - - //// Load all macroPropertyTypes - //var macroPropertyTypes = new Hashtable(); - //var macroPropertyIds = new Hashtable(); - - //var macroPropTypes = ParameterEditorResolver.Current.ParameterEditors.ToArray(); - - //foreach (var mpt in macroPropTypes) - //{ - // macroPropertyIds.Add(mpt.Alias, mpt.Id.ToString()); - // macroPropertyTypes.Add(mpt.Alias, mpt.BaseType); - //} - var changed = false; - - foreach (ListItem li in MacroProperties.Items) - { - if (li.Selected && MacroHasProperty(macroObject, li.Text.Substring(0, li.Text.IndexOf(" ", StringComparison.Ordinal)).ToLower()) == false) - { - result += "

  • Added: " + SpaceCamelCasing(li.Text) + "
  • "; - var macroPropertyTypeAlias = GetMacroTypeFromClrType(li.Value); - - macroObject.Properties.Add(new Umbraco.Core.Models.MacroProperty - { - Name = SpaceCamelCasing(li.Text), - Alias = li.Text.Substring(0, li.Text.IndexOf(" ", StringComparison.Ordinal)), - EditorAlias = macroPropertyTypeAlias - }); - - changed = true; - } - else if (li.Selected) - { - result += "
  • Skipped: " + SpaceCamelCasing(li.Text) + " (already exists as a parameter)
  • "; - } - } - - if (changed) - { - Current.Services.MacroService.Save(macroObject); - } - - ChooseProperties.Visible = false; - ConfigProperties.Visible = true; - resultLiteral.Text = result; - } - - private static bool MacroHasProperty(IMacro macroObject, string propertyAlias) - { - return macroObject.Properties.Any(mp => mp.Alias.ToLower() == propertyAlias); - } - - private static string SpaceCamelCasing(string text) - { - var tempString = text.Substring(0, 1).ToUpper(); - for (var i = 1; i < text.Length; i++) - { - if (text.Substring(i, 1) == " ") - break; - if (text.Substring(i, 1).ToUpper() == text.Substring(i, 1)) - tempString += " "; - tempString += text.Substring(i, 1); - } - return tempString; - } - - private static string GetMacroTypeFromClrType(string baseTypeName) - { - switch (baseTypeName) - { - case "Int32": - return Constants.PropertyEditors.Aliases.Integer; - case "Decimal": - //we previously only had an integer editor too! - this would of course - // fail if someone enters a real long number - return Constants.PropertyEditors.Aliases.Integer; - case "Boolean": - return Constants.PropertyEditors.Aliases.Boolean; - case "String": - default: - return Constants.PropertyEditors.Aliases.TextBox; - } - } - - } - -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.designer.cs deleted file mode 100644 index 9b8f124443..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.designer.cs +++ /dev/null @@ -1,69 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.developer { - - - public partial class assemblyBrowser { - - /// - /// AssemblyName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label AssemblyName; - - /// - /// ChooseProperties control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel ChooseProperties; - - /// - /// MacroProperties control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList MacroProperties; - - /// - /// Button1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button Button1; - - /// - /// ConfigProperties control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel ConfigProperties; - - /// - /// resultLiteral control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal resultLiteral; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs index 163e1da2b8..1f2fcf515c 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs @@ -47,22 +47,9 @@ namespace umbraco.cms.presentation.developer ClientTools .SyncTree("-1," + _macro.Id, false); - string tempMacroAssembly = _macro.ControlAssembly ?? ""; string tempMacroType = _macro.ControlType ?? ""; - PopulateFieldsOnLoad(_macro, tempMacroAssembly, tempMacroType); - - // Check for assemblyBrowser - if (tempMacroType.IndexOf(".ascx", StringComparison.Ordinal) > 0) - assemblyBrowserUserControl.Controls.Add( - new LiteralControl("
    ")); - else if (tempMacroType != string.Empty && tempMacroAssembly != string.Empty) - assemblyBrowser.Controls.Add( - new LiteralControl("
    ")); + PopulateFieldsOnLoad(_macro, tempMacroType); // Load elements from macro macroPropertyBind(); @@ -83,12 +70,11 @@ namespace umbraco.cms.presentation.developer /// /// /// - protected virtual void PopulateFieldsOnLoad(IMacro macro, string macroAssemblyValue, string macroTypeValue) + protected virtual void PopulateFieldsOnLoad(IMacro macro, string macroTypeValue) { macroName.Text = macro.Name; macroAlias.Text = macro.Alias; macroKey.Text = macro.Key.ToString(); - macroXslt.Text = macro.XsltPath; cachePeriod.Text = macro.CacheDuration.ToInvariantString(); macroRenderContent.Checked = macro.DontRender == false; macroEditor.Checked = macro.UseInEditor; @@ -96,9 +82,8 @@ namespace umbraco.cms.presentation.developer cachePersonalized.Checked = macro.CacheByMember; // Populate either user control or custom control - if (macroTypeValue != string.Empty && macroAssemblyValue != string.Empty) + if (macroTypeValue != string.Empty) { - macroAssembly.Text = macroAssemblyValue; macroType.Text = macroTypeValue; } else @@ -119,9 +104,7 @@ namespace umbraco.cms.presentation.developer macro.CacheDuration = macroCachePeriod; macro.Alias = macroAlias.Text; macro.Name = macroName.Text; - macro.ControlAssembly = macroAssemblyValue; macro.ControlType = macroTypeValue; - macro.XsltPath = macroXslt.Text; } private static void GetXsltFilesFromDir(string orgPath, string path, ArrayList files) @@ -229,7 +212,7 @@ namespace umbraco.cms.presentation.developer return Convert.ToBoolean(isChecked); } - public void AddChooseList(Object sender, EventArgs e) + public void AddChooseList(object sender, EventArgs e) { if (IsPostBack == false) { @@ -341,18 +324,6 @@ namespace umbraco.cms.presentation.developer ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Save, "Macro saved", ""); - // Check for assemblyBrowser - if (tempMacroType.IndexOf(".ascx", StringComparison.Ordinal) > 0) - assemblyBrowserUserControl.Controls.Add( - new LiteralControl("
    ")); - else if (tempMacroType != string.Empty && tempMacroAssembly != string.Empty) - assemblyBrowser.Controls.Add( - new LiteralControl("
    ")); - macroPropertyBind(); } @@ -418,16 +389,7 @@ namespace umbraco.cms.presentation.developer /// To modify move field declaration from designer file to code-behind file. /// protected global::Umbraco.Web._Legacy.Controls.Pane Pane1_2; - - /// - /// macroXslt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox macroXslt; - + /// /// xsltFiles control. /// @@ -454,16 +416,7 @@ namespace umbraco.cms.presentation.developer /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.DropDownList userControlList; - - /// - /// assemblyBrowserUserControl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder assemblyBrowserUserControl; - + /// /// macroAssembly control. /// @@ -481,16 +434,7 @@ namespace umbraco.cms.presentation.developer /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.TextBox macroType; - - /// - /// assemblyBrowser control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder assemblyBrowser; - + /// /// Pane1_3 control. /// diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/editXslt.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/editXslt.aspx.cs deleted file mode 100644 index 82bc42a2e6..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/editXslt.aspx.cs +++ /dev/null @@ -1,198 +0,0 @@ -using Umbraco.Core.Services; -using System; -using System.Collections.Generic; -using System.IO; -using System.Web.UI; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web._Legacy.Controls; -using umbraco.cms.presentation.Trees; -using Umbraco.Web.UI.Pages; - -namespace umbraco.cms.presentation.developer -{ - /// - /// Summary description for editXslt. - /// - [WebformsPageTreeAuthorize(Constants.Trees.Xslt)] - public partial class editXslt : UmbracoEnsuredPage - { - - protected PlaceHolder buttons; - - protected MenuButton SaveButton; - - protected void Page_Load(object sender, EventArgs e) - { - if (!IsPostBack) - { - string file = Request.QueryString["file"]; - string path = BaseTree.GetTreePathFromFilePath(file, false, true); - ClientTools - .SyncTree(path, false); - } - } - - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - SaveButton = UmbracoPanel1.Menu.NewButton(); - SaveButton.ToolTip = "Save Xslt File"; - SaveButton.Text = Services.TextService.Localize("save"); - SaveButton.ButtonType = MenuButtonType.Primary; - SaveButton.ID = "save"; - SaveButton.CssClass = "client-side"; - - var code = UmbracoPanel1.NewTabPage("xslt"); - code.Controls.Add(pane1); - - var props = UmbracoPanel1.NewTabPage("properties"); - props.Controls.Add(pane2); - - var tmp = editorSource.Menu.NewIcon(); - tmp.ImageURL = IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/images/editor/insField.GIF"; - tmp.OnClickCommand = ClientTools.Scripts.OpenModalWindow(IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/developer/xslt/xsltinsertvalueof.aspx?objectId=" + editorSource.ClientID, "Insert value", 750, 250); - //"umbracoInsertField(document.getElementById('editorSource'), 'xsltInsertValueOf', '','felt', 750, 230, '');"; - tmp.AltText = "Insert xslt:value-of"; - - editorSource.Menu.InsertSplitter(); - - tmp = editorSource.Menu.NewIcon(); - tmp.ImageURL = SystemDirectories.Umbraco + "/images/editor/insMemberItem.GIF"; - tmp.OnClickCommand = "UmbEditor.Insert('\\n', '" + editorSource.ClientID + "'); return false;"; - tmp.AltText = "Insert xsl:variable"; - - editorSource.Menu.InsertSplitter(); - - tmp = editorSource.Menu.NewIcon(); - tmp.ImageURL = SystemDirectories.Umbraco + "/images/editor/insChildTemplateNew.GIF"; - tmp.OnClickCommand = "UmbEditor.Insert('\\n', '\\n\\n', '" + editorSource.ClientID + "'); return false;"; - tmp.AltText = "Insert xsl:if"; - - tmp = editorSource.Menu.NewIcon(); - tmp.ImageURL = SystemDirectories.Umbraco + "/images/editor/insChildTemplateNew.GIF"; - tmp.OnClickCommand = "UmbEditor.Insert('\\n', '\\n\\n', '" + editorSource.ClientID + "'); return false;"; - tmp.AltText = "Insert xsl:for-each"; - - editorSource.Menu.InsertSplitter(); - - tmp = editorSource.Menu.NewIcon(); - tmp.ImageURL = SystemDirectories.Umbraco + "/images/editor/insFieldByLevel.GIF"; - tmp.OnClickCommand = "UmbEditor.Insert('\\n\\n', '\\n\\n\\n\\n\\n', '" + editorSource.ClientID + "'); return false;"; - tmp.AltText = "Insert xsl:choose"; - - editorSource.Menu.InsertSplitter(); - - tmp = editorSource.Menu.NewIcon(); - tmp.ImageURL = SystemDirectories.Umbraco + "/images/editor/xslVisualize.GIF"; - tmp.OnClickCommand = "xsltVisualize();"; - tmp.AltText = "Visualize XSLT"; - - - // Add source and filename - var file = IOHelper.MapPath(SystemDirectories.Xslt + "/" + Request.QueryString["file"]); - - // validate file - IOHelper.ValidateEditPath(file, SystemDirectories.Xslt); - // validate extension - IOHelper.ValidateFileExtension(file, new List() { "xslt", "xsl" }); - - - xsltFileName.Text = file.Replace(IOHelper.MapPath(SystemDirectories.Xslt), "").Substring(1).Replace(@"\", "/"); - - StreamReader SR; - string S; - SR = File.OpenText(file); - - S = SR.ReadToEnd(); - SR.Close(); - - editorSource.Text = S; - } - - - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender(e); - - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference(IOHelper.ResolveUrl(SystemDirectories.WebServices) + "/codeEditorSave.asmx")); - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference(IOHelper.ResolveUrl(SystemDirectories.WebServices) + "/legacyAjaxCalls.asmx")); - } - - - /// - /// JsInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; - - /// - /// UmbracoPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.TabView UmbracoPanel1; - - /// - /// Pane1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane1; - protected global::Umbraco.Web._Legacy.Controls.Pane pane2; - - /// - /// pp_filename control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_filename; - - /// - /// xsltFileName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox xsltFileName; - - /// - /// pp_errorMsg control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_errorMsg; - - /// - /// editorSource control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.CodeArea editorSource; - - /// - /// editorJs control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal editorJs; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/getXsltStatus.asmx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/getXsltStatus.asmx deleted file mode 100644 index 7d5385af8f..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/getXsltStatus.asmx +++ /dev/null @@ -1 +0,0 @@ -<%@ WebService Language="c#" Codebehind="getXsltStatus.asmx.cs" Class="umbraco.developer.getXsltStatus" %> diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/getXsltStatus.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/getXsltStatus.asmx.cs deleted file mode 100644 index 99a74b0d8c..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/getXsltStatus.asmx.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections; -using System.ComponentModel; -using System.Data; -using System.Diagnostics; -using System.Web; -using System.Web.Services; -using System.Xml; -using System.IO; -using Umbraco.Core.IO; -using umbraco.presentation.webservices; -using Umbraco.Web.WebServices; - -namespace umbraco.developer -{ - /// - /// Summary description for getXsltStatus. - /// - [WebService(Namespace="http://umbraco.org/webservices")] - public class getXsltStatus : UmbracoAuthorizedWebService - { - public getXsltStatus() - { - //CODEGEN: This call is required by the ASP.NET Web Services Designer - InitializeComponent(); - } - - // TODO: Security-check - [WebMethod] - public XmlDocument FilesFromDirectory(string dir) - { - AuthorizeRequest(true); - - XmlDocument xd = new XmlDocument(); - xd.LoadXml(""); - foreach (string file in System.IO.Directory.GetFiles(IOHelper.MapPath(SystemDirectories.Umbraco + "/xslt/" + dir), "*.xsl*")) - { - FileInfo fi = new FileInfo(file); - FileAttributes fa = fi.Attributes; - XmlElement fileXml = xd.CreateElement("file"); - fileXml.SetAttribute("name", fi.Name); - //fileXml.SetAttribute("created", fi.CreationTimeUtc); - fileXml.SetAttribute("modified", fi.LastWriteTimeUtc.ToString()); - fileXml.SetAttribute("size", fi.Length.ToString()); - xd.DocumentElement.AppendChild((XmlNode) fileXml); - } - return xd; - } - - #region Component Designer generated code - - //Required by the Web Services Designer - private IContainer components = null; - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - - /// - /// Clean up any resources being used. - /// - protected override void Dispose( bool disposing ) - { - if(disposing && components != null) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #endregion - - // WEB SERVICE EXAMPLE - // The HelloWorld() example service returns the string Hello World - // To build, uncomment the following lines then save and build the project - // To test this web service, press F5 - -// [WebMethod] -// public string HelloWorld() -// { -// return "Hello World"; -// } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx deleted file mode 100644 index b719951aed..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx +++ /dev/null @@ -1,44 +0,0 @@ -<%@ Page Language="c#" Codebehind="xsltChooseExtension.aspx.cs" MasterPageFile="../../masterpages/umbracoDialog.Master" AutoEventWireup="True" - Inherits="umbraco.developer.xsltChooseExtension" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - - - - - - - - - - - - - - -

    - <%= Services.TextService.Localize("or") %> <%= Services.TextService.Localize("cancel") %> -

    - -
    \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.cs deleted file mode 100644 index e258a69a65..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; -using System.Text; -using System.Web.UI; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Web.UI.Pages; - -namespace umbraco.developer -{ - /// - /// Summary description for xsltChooseExtension. - /// - [WebformsPageTreeAuthorize(Constants.Trees.Xslt)] - public partial class xsltChooseExtension : UmbracoEnsuredPage - { - - protected void Page_Load(object sender, System.EventArgs e) - { - SortedList> ht = GetXsltAssembliesAndMethods(); - if (!IsPostBack) - { - assemblies.Attributes.Add("onChange", "document.forms[0].submit()"); - foreach(string s in ht.Keys) - assemblies.Items.Add(new ListItem(s)); - - // Select the umbraco extensions as default - assemblies.Items[0].Selected = true; - } - - string selectedMethod = ""; - if (methods.SelectedValue != "") - { - selectedMethod = methods.SelectedValue; - PlaceHolderParamters.Controls.Clear(); - PlaceHolderParamters.Controls.Add(new LiteralControl("
    " + assemblies.SelectedItem + ":" + methods.SelectedValue.Substring(0, methods.SelectedValue.IndexOf("(")) + "(")); - PlaceHolderParamters.Controls.Add(new LiteralControl("")); - - int counter = 0; - string[] props = methods.SelectedValue.Substring(methods.SelectedValue.IndexOf("(") + 1, methods.SelectedValue.IndexOf(")") - methods.SelectedValue.IndexOf("(") - 1).Split(','); - - foreach (string s in props) - { - if (s.Trim() != "") - { - counter++; - TextBox t = new TextBox(); - t.ID = "param" + Guid.NewGuid().ToString(); - t.Text = s.Trim(); - t.TabIndex = (short) counter; - t.Attributes.Add("onFocus", "if (this.value == '" + s.Trim() + "') this.value = '';"); - t.Attributes.Add("onBlur", "if (this.value == '') this.value = '" + s.Trim() + "'"); - t.Attributes.Add("style", "width:80px;"); - PlaceHolderParamters.Controls.Add(t); - - if(counter < props.Length) - PlaceHolderParamters.Controls.Add(new LiteralControl(",")); - } - } - - counter++; - - //PlaceHolderParamters.Controls.Add(new LiteralControl(")

    ")); - PlaceHolderParamters.Controls.Add(new LiteralControl(")")); - bt_insert.Enabled = true; - } - else - PlaceHolderParamters.Controls.Clear(); - - - if (assemblies.SelectedValue != "") - { - methods.Items.Clear(); - methods.Items.Add(new ListItem("Choose method", "")); - methods.Attributes.Add("onChange", "document.forms[0].submit()"); - List methodList = ht[assemblies.SelectedValue]; - foreach (string method in methodList) - { - ListItem li = new ListItem(method); - if (method == selectedMethod) - li.Selected = true; - methods.Items.Add(li); - } - } - } - - /// - /// Gets the XSLT assemblies and their methods. - /// - /// A list of assembly names linked to a list of method signatures. - private SortedList> GetXsltAssembliesAndMethods() - { - SortedList> _tempAssemblies = new SortedList>(); - - // add all extensions definied by macro - foreach(KeyValuePair extension in Umbraco.Web.Macros.XsltMacroEngine.GetXsltExtensions()) - _tempAssemblies.Add(extension.Key, GetStaticMethods(extension.Value.GetType())); - - // add the Umbraco library (not included in macro extensions) - _tempAssemblies.Add("umbraco.library", GetStaticMethods(typeof(umbraco.library))); - - return _tempAssemblies; - - } - - /// - /// Gets the static methods of the specified type, alphabetically sorted. - /// - /// The type. - /// A sortd list with method signatures. - private List GetStaticMethods(Type type) - { - List methods = new List(); - foreach (MethodInfo method in type.GetMethods()) - { - if (method.IsStatic) - { - // add method name to signature - StringBuilder methodSignature = new StringBuilder(method.Name); - - // add parameters to signature - methodSignature.Append('('); - ParameterInfo[] parameters = method.GetParameters(); - for(int i=0; i - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - ///
    - private void InitializeComponent() - { - } - #endregion - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.designer.cs deleted file mode 100644 index 2b5efd94bf..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.designer.cs +++ /dev/null @@ -1,51 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.developer { - - - public partial class xsltChooseExtension { - - /// - /// assemblies control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList assemblies; - - /// - /// methods control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList methods; - - /// - /// PlaceHolderParamters control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder PlaceHolderParamters; - - /// - /// bt_insert control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_insert; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx deleted file mode 100644 index 34142b2805..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx +++ /dev/null @@ -1,61 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoDialog.Master" Codebehind="xsltInsertValueOf.aspx.cs" AutoEventWireup="True" Inherits="umbraco.developer.xsltInsertValueOf" %> -<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - - - - - - - - - - - - - - - - - -

    - <%= Services.TextService.Localize("or") %> <%= Services.TextService.Localize("cancel") %> -

    -
    \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs deleted file mode 100644 index 1a0c1d1c90..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections; -using System.Linq; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Web.UI.Pages; - -namespace umbraco.developer -{ - /// - /// Summary description for xsltInsertValueOf. - /// - [WebformsPageTreeAuthorize(Constants.Trees.Xslt)] - public partial class xsltInsertValueOf : UmbracoEnsuredPage - { - protected void Page_Load(object sender, System.EventArgs e) - { - ArrayList preValuesSource = new ArrayList(); - - // Attributes - string[] attributes = {"@id", "@parentID", "@level", "@writerID", "@nodeType", "@template", "@sortOrder", "@createDate", "@creatorName", "@updateDate", "@nodeName", "@urlName", "@writerName", "@nodeTypeAlias", "@path"}; - foreach (string att in attributes) - preValuesSource.Add(att); - - // generic properties - string existingGenProps = ","; - var exclude = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray(); - - var propertyTypes = Services.ContentTypeService.GetAllPropertyTypeAliases(); - - foreach (var ptAlias in propertyTypes.Where(x => exclude.Contains(x) == false)) - { - if (!existingGenProps.Contains("," + ptAlias + ",")) - { - preValuesSource.Add(ptAlias); - - - existingGenProps += ptAlias + ","; - } - } - - - preValuesSource.Sort(); - preValues.DataSource = preValuesSource; - preValues.DataBind(); - preValues.Items.Insert(0, new ListItem("Prevalues...", "")); - - preValues.Attributes.Add("onChange", "if (this.value != '') document.getElementById('" + valueOf.ClientID + "').value = this.value"); - - if(!String.IsNullOrEmpty(Request.QueryString["value"])) - valueOf.Text = Request.QueryString["value"]; - } - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.designer.cs deleted file mode 100644 index d02006dd22..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.designer.cs +++ /dev/null @@ -1,51 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.developer { - - - public partial class xsltInsertValueOf { - - /// - /// JsInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; - - /// - /// valueOf control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox valueOf; - - /// - /// preValues control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList preValues; - - /// - /// disableOutputEscaping control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox disableOutputEscaping; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx deleted file mode 100644 index 74c40eca64..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx +++ /dev/null @@ -1,55 +0,0 @@ -<%@ Page Language="C#" MasterPageFile="../../masterpages/umbracoDialog.Master" AutoEventWireup="true" - CodeBehind="xsltVisualize.aspx.cs" ValidateRequest="false" Inherits="umbraco.presentation.umbraco.developer.Xslt.xsltVisualize" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> -<%@ Register TagPrefix="cc2" Namespace="umbraco.controls" Assembly="Umbraco.Web" %> - - - - - - - - - - - -

    - -
    -
    - -

    - -

    - - - -
    - -
    diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.cs deleted file mode 100644 index fe4bca93e5..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; - -using System.Text; -using System.Xml; -using System.IO; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.UI.Pages; - -namespace umbraco.presentation.umbraco.developer.Xslt -{ - [WebformsPageTreeAuthorize(Constants.Trees.Xslt)] - public partial class xsltVisualize : UmbracoEnsuredPage - { - private const string XsltVisualizeCookieName = "UMB_XSLTVISPG"; - - protected void Page_Load(object sender, EventArgs e) - { - if (!IsPostBack) - { - // Check if cookie exists in the current request. - // zb-00004 #29956 : refactor cookies names & handling - if (Request.HasCookieValue(XsltVisualizeCookieName)) - contentPicker.Value = Request.GetCookieValue(XsltVisualizeCookieName); - } - - } - - protected void visualizeDo_Click(object sender, EventArgs e) - { - // get xslt file - string xslt; - if (xsltSelection.Value.Contains("", xsltSelection.Value); - xslt = Umbraco.Web.Macros.XsltMacroEngine.AddXsltExtensionsToHeader(xslt); - } - - int pageId; - if (int.TryParse(contentPicker.Value, out pageId) == false) - pageId = -1; - - // transform - string xsltResult; - try - { - xsltResult = Umbraco.Web.Macros.XsltMacroEngine.TestXsltTransform(Current.ProfilingLogger, xslt, pageId); - } - catch (Exception ee) - { - xsltResult = string.Format( - "

    Error parsing the XSLT:

    {0}

    ", - ee.ToString()); - } - - visualizeContainer.Visible = true; - - // update output - visualizeArea.Text = !String.IsNullOrEmpty(xsltResult) ? "
    " + xsltResult + "
    " : "

    The XSLT didn't generate any output

    "; - - - // add cookie with current page - // zb-00004 #29956 : refactor cookies names & handling - Response.Cookies.Set(new HttpCookie(XsltVisualizeCookieName, contentPicker.Value) - { - Expires = DateTime.Now + TimeSpan.FromMinutes(20) - }); - } - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.designer.cs deleted file mode 100644 index cfc124ef7a..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.designer.cs +++ /dev/null @@ -1,87 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.umbraco.developer.Xslt { - - - public partial class xsltVisualize { - - /// - /// Pane1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel PropertyPanel1; - - /// - /// xsltSelection control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden xsltSelection; - - /// - /// contentPicker control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.controls.ContentPicker contentPicker; - - /// - /// visualizeDo control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button visualizeDo; - - /// - /// visualizeContainer control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane visualizeContainer; - - /// - /// visualizePanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel visualizePanel; - - /// - /// visualizeArea control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal visualizeArea; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/InlineXslt.xsltTemplate b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/InlineXslt.xsltTemplate deleted file mode 100644 index b26ca5da89..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/InlineXslt.xsltTemplate +++ /dev/null @@ -1,14 +0,0 @@ - - ]> - - - - - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs index b015fdd02a..0439cfe89d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs @@ -82,42 +82,7 @@ namespace umbraco.presentation.templateControls get { return (string)ViewState["TextIfEmpty"] ?? String.Empty; } set { ViewState["TextIfEmpty"] = value; } } - - /// - /// Gets or sets the XPath expression used for the inline XSLT transformation. - /// - /// - /// The XPath expression, or an empty string to disable XSLT transformation. - /// The code {0} is used as a placeholder for the rendered field contents. - /// - [Bindable(true)] - [Category("Umbraco")] - [DefaultValue("")] - [Localizable(true)] - public string Xslt - { - get { return (string)ViewState["Xslt"] ?? String.Empty; } - set { ViewState["Xslt"] = value; } - } - - /// - /// Gets or sets a value indicating whether XML entity escaping of the XSLT transformation output is disabled. - /// - /// true HTML escaping is disabled; otherwise, false (default). - /// - /// This corresponds value to the disable-output-escaping parameter - /// of the XSLT value-of element. - /// - [Bindable(true)] - [Category("Umbraco")] - [DefaultValue(false)] - [Localizable(true)] - public bool XsltDisableEscaping - { - get { return ViewState["XsltEscape"] == null ? false : (bool)ViewState["XsltEscape"]; } - set { ViewState["XsltEscape"] = value; } - } - + [Bindable(true)] [Category("Umbraco")] [DefaultValue("")] diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs index 06c09225e2..13414c7648 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs @@ -56,12 +56,10 @@ namespace umbraco.presentation.templateControls // parse macros and execute the XSLT transformation on the result if not empty string renderOutput = renderOutputWriter.ToString(); - string xsltTransformedOutput = renderOutput.Trim().Length == 0 - ? String.Empty - : XsltTransform(item.Xslt, renderOutput, item.XsltDisableEscaping); + renderOutput = renderOutput.Trim().Length == 0 ? string.Empty : renderOutput; // handle text before/after - xsltTransformedOutput = AddBeforeAfterText(xsltTransformedOutput, helper.FindAttribute(item.LegacyAttributes, "insertTextBefore"), helper.FindAttribute(item.LegacyAttributes, "insertTextAfter")); - string finalResult = xsltTransformedOutput.Trim().Length > 0 ? xsltTransformedOutput : GetEmptyText(item); + renderOutput = AddBeforeAfterText(renderOutput, helper.FindAttribute(item.LegacyAttributes, "insertTextBefore"), helper.FindAttribute(item.LegacyAttributes, "insertTextAfter")); + string finalResult = renderOutput.Trim().Length > 0 ? renderOutput : GetEmptyText(item); //Don't parse urls if a content item is assigned since that is taken care // of with the value converters @@ -192,48 +190,6 @@ namespace umbraco.presentation.templateControls } - /// - /// Transforms the content using the XSLT attribute, if provided. - /// - /// The xpath expression. - /// The item's rendered content. - /// if set to true, escaping is disabled. - /// The transformed content if the XSLT attribute is present, otherwise the original content. - protected virtual string XsltTransform(string xpath, string itemData, bool disableEscaping) - { - if (!String.IsNullOrEmpty(xpath)) - { - // XML-encode the expression and add the itemData parameter to it - string xpathEscaped = xpath.Replace("<", "<").Replace(">", ">").Replace("\"", """); - string xpathExpression = string.Format(xpathEscaped, "$itemData"); - - // prepare support for XSLT extensions - StringBuilder namespaceList = new StringBuilder(); - StringBuilder namespaceDeclaractions = new StringBuilder(); - foreach (KeyValuePair extension in Umbraco.Web.Macros.XsltMacroEngine.GetXsltExtensions()) - { - namespaceList.Append(extension.Key).Append(' '); - namespaceDeclaractions.AppendFormat("xmlns:{0}=\"urn:{0}\" ", extension.Key); - } - - // add the XSLT expression into the full XSLT document, together with the needed parameters - string xslt = string.Format(Umbraco.Web.umbraco.presentation.umbraco.templateControls.Resources.InlineXslt, xpathExpression, disableEscaping ? "yes" : "no", - namespaceList, namespaceDeclaractions); - - // create the parameter - Dictionary parameters = new Dictionary(1); - parameters.Add("itemData", itemData); - - // apply the XSLT transformation - using (var xslReader = new XmlTextReader(new StringReader(xslt))) - { - var transform = Umbraco.Web.Macros.XsltMacroEngine.GetXsltTransform(xslReader, false); - return Umbraco.Web.Macros.XsltMacroEngine.ExecuteItemRenderer(Current.ProfilingLogger, transform, itemData); - } - } - return itemData; - } - protected string AddBeforeAfterText(string text, string before, string after) { if (!String.IsNullOrEmpty(text)) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.Designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.Designer.cs deleted file mode 100644 index ab69886621..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.Designer.cs +++ /dev/null @@ -1,84 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.umbraco.presentation.umbraco.templateControls { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Web.umbraco.presentation.umbraco.templateControls.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8"?> - ///<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#x00A0;"> ]> - ///<xsl:stylesheet - /// version="1.0" - /// xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - /// xmlns:msxml="urn:schemas-microsoft-com:xslt" - /// xmlns:umbraco.library="urn:umbraco.library" - /// {3} - /// exclude-result-prefixes="msxml umbraco.library {2}"> - ///<xsl:output method="xml" omit-xml-declaration="yes"/> - ///<xsl:param name="currentPage"/> - ///<xsl:param name="itemData"/> - ///<xsl:template match="/"><xsl:value-of select="{0}" disa [rest of string was truncated]";. - /// - internal static string InlineXslt { - get { - return ResourceManager.GetString("InlineXslt", resourceCulture); - } - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.resx b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.resx deleted file mode 100644 index 00bf9d42e1..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.resx +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - inlinexslt.xsltTemplate;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx deleted file mode 100644 index 46767336e7..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx +++ /dev/null @@ -1 +0,0 @@ -<%@ WebService Language="C#" CodeBehind="codeEditorSave.asmx.cs" Class="umbraco.presentation.webservices.codeEditorSave" %> diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx.cs deleted file mode 100644 index f39dd051be..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Text.RegularExpressions; -using System.Web.Script.Services; -using System.Web.Services; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web.WebServices; -using Umbraco.Web.Macros; - -namespace umbraco.presentation.webservices -{ - /// - /// Summary description for codeEditorSave - /// - [WebService(Namespace = "http://tempuri.org/")] - [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] - [ToolboxItem(false)] - [ScriptService] - public class codeEditorSave : UmbracoAuthorizedWebService - { - [WebMethod] - public string SaveXslt(string fileName, string oldName, string fileContents, bool ignoreDebugging) - { - if (AuthorizeRequest(Constants.Applications.Developer.ToString())) - { - IOHelper.EnsurePathExists(SystemDirectories.Xslt); - - // validate file - IOHelper.ValidateEditPath(IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName), - SystemDirectories.Xslt); - // validate extension - IOHelper.ValidateFileExtension(IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName), - new List() { "xsl", "xslt" }); - - StreamWriter SW; - string tempFileName = IOHelper.MapPath(SystemDirectories.Xslt + "/" + DateTime.Now.Ticks + "_temp.xslt"); - SW = File.CreateText(tempFileName); - SW.Write(fileContents); - SW.Close(); - - // Test the xslt - string errorMessage = ""; - - if (ignoreDebugging == false) - { - try - { - if (UmbracoContext.ContentCache.HasContent()) - XsltMacroEngine.TestXsltTransform(ProfilingLogger, fileContents); - - /* - // Check if there's any documents yet - string xpath = "/root/*"; - if (content.Instance.XmlContent.SelectNodes(xpath).Count > 0) - { - var macroXML = new XmlDocument(); - macroXML.LoadXml(""); - - var macroXSLT = new XslCompiledTransform(); - var umbPage = new page(content.Instance.XmlContent.SelectSingleNode("//* [@parentID = -1]")); - - var xslArgs = macro.AddMacroXsltExtensions(); - var lib = new library(umbPage); - xslArgs.AddExtensionObject("urn:umbraco.library", lib); - HttpContext.Current.Trace.Write("umbracoMacro", "After adding extensions"); - - // Add the current node - xslArgs.AddParam("currentPage", "", library.GetXmlNodeById(umbPage.PageID.ToString())); - - HttpContext.Current.Trace.Write("umbracoMacro", "Before performing transformation"); - - // Create reader and load XSL file - // We need to allow custom DTD's, useful for defining an ENTITY - var readerSettings = new XmlReaderSettings(); - readerSettings.ProhibitDtd = false; - using (var xmlReader = XmlReader.Create(tempFileName, readerSettings)) - { - var xslResolver = new XmlUrlResolver(); - xslResolver.Credentials = CredentialCache.DefaultCredentials; - macroXSLT.Load(xmlReader, XsltSettings.TrustedXslt, xslResolver); - xmlReader.Close(); - // Try to execute the transformation - var macroResult = new HtmlTextWriter(new StringWriter()); - macroXSLT.Transform(macroXML, xslArgs, macroResult); - macroResult.Close(); - - File.Delete(tempFileName); - } - } - */ - else - { - //errorMessage = Services.TextService.Localize("developer/xsltErrorNoNodesPublished"); - File.Delete(tempFileName); - //base.speechBubble(speechBubbleIcon.info, Services.TextService.Localize("errors/xsltErrorHeader"), "Unable to validate xslt as no published content nodes exist."); - } - } - catch (Exception errorXslt) - { - File.Delete(tempFileName); - - errorMessage = (errorXslt.InnerException ?? errorXslt).ToString(); - - // Full error message - errorMessage = errorMessage.Replace("\n", "
    \n"); - //closeErrorMessage.Visible = true; - - // Find error - var m = Regex.Matches(errorMessage, @"\d*[^,],\d[^\)]", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - foreach (Match mm in m) - { - string[] errorLine = mm.Value.Split(','); - - if (errorLine.Length > 0) - { - var theErrorLine = int.Parse(errorLine[0]); - var theErrorChar = int.Parse(errorLine[1]); - - errorMessage = "Error in XSLT at line " + errorLine[0] + ", char " + errorLine[1] + - "
    "; - errorMessage += ""; - - var xsltText = fileContents.Split("\n".ToCharArray()); - for (var i = 0; i < xsltText.Length; i++) - { - if (i >= theErrorLine - 3 && i <= theErrorLine + 1) - if (i + 1 == theErrorLine) - { - errorMessage += "" + (i + 1) + ": >>>  " + - Server.HtmlEncode(xsltText[i].Substring(0, theErrorChar)); - errorMessage += - "" + - Server.HtmlEncode( - xsltText[i].Substring(theErrorChar, - xsltText[i].Length - theErrorChar)). - Trim() + ""; - errorMessage += " <<<
    "; - } - else - errorMessage += (i + 1) + ":      " + - Server.HtmlEncode(xsltText[i]) + "
    "; - } - errorMessage += "
    "; - } - } - } - } - - if (errorMessage == "" && fileName.ToLower().EndsWith(".xslt")) - { - //Hardcoded security-check... only allow saving files in xslt directory... - var savePath = IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName); - - if (savePath.StartsWith(IOHelper.MapPath(SystemDirectories.Xslt + "/"))) - { - //deletes the old xslt file - if (fileName != oldName) - { - - var p = IOHelper.MapPath(SystemDirectories.Xslt + "/" + oldName); - if (File.Exists(p)) - File.Delete(p); - } - - SW = File.CreateText(savePath); - SW.Write(fileContents); - SW.Close(); - errorMessage = "true"; - - - } - else - { - errorMessage = "Illegal path"; - } - } - - File.Delete(tempFileName); - - return errorMessage; - } - return "false"; - } - } -} From 92d88d5a4c4fe29a9db9275b54fe84922e46efc9 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 30 Apr 2018 16:18:35 +0200 Subject: [PATCH 62/97] Publish dialog should show only variants in Draft state --- .../overlays/publish/publish.controller.js | 49 +++++++++++++------ .../common/overlays/publish/publish.html | 2 +- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js index a489e9927d..c0e457e0b9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js @@ -1,23 +1,27 @@ (function () { "use strict"; - function PublishController($scope, $timeout) { + function PublishController($scope, eventsService) { var vm = this; - vm.variants = $scope.model.variants; + var variants = $scope.model.variants; vm.changeSelection = changeSelection; + vm.loading = true; + + vm.dirtyVariants = []; + vm.pristineVariants = []; //watch this model, if it's reset, then re init - $scope.$watch(function() { - return $scope.model.variants; - }, - function(newVal, oldVal) { + $scope.$watch(function () { + return $scope.model.variants; + }, + function (newVal, oldVal) { vm.variants = newVal; if (oldVal && oldVal.length) { //re-bind the selections for (var i = 0; i < oldVal.length; i++) { - var found = _.find(vm.variants, function(v) { - return v.language.id == oldVal[i].language.id; + var found = _.find(variants, function (v) { + return v.language.id === oldVal[i].language.id; }); if (found) { found.publish = oldVal[i].publish; @@ -28,24 +32,39 @@ }); function changeSelection(variant) { - var firstSelected = _.find(vm.variants, function(v) { + var firstSelected = _.find(variants, function (v) { return v.publish; }); $scope.model.disableSubmitButton = !firstSelected; //disable submit button if there is none selected } function onInit() { - _.each(vm.variants, - function (v) { - v.compositeId = v.language.id + "_" + (v.segment ? v.segment : ""); - v.htmlId = "publish_variant_" + v.compositeId; + _.each(variants, + function (variant) { + variant.compositeId = variant.language.id + "_" + (variant.segment ? variant.segment : ""); + variant.htmlId = "publish_variant_" + variant.compositeId; + + //append Draft state to variant + if (variant.isEdited === true && !variant.state.includes("Draft")) { + variant.state += ", Draft"; + vm.dirtyVariants.push(variant); + } else if (variant.isEdited === true) { + vm.dirtyVariants.push(variant); + } else { + vm.pristineVariants.push(variant); + } }); + + vm.loading = false; + + console.log("Dirty Variants", vm.dirtyVariants); + //now sort it so that the current one is at the top - vm.variants = _.sortBy(vm.variants, function(v) { + vm.dirtyVariants = _.sortBy(vm.dirtyVariants, function (v) { return v.current ? 0 : 1; }); //ensure that the current one is selected - vm.variants[0].publish = true; + vm.dirtyVariants[0].publish = true; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html index ed7f32fc25..f0d721ec04 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html @@ -9,7 +9,7 @@
    -
    +