diff --git a/src/Umbraco.Core/CoreRuntimeComponent.cs b/src/Umbraco.Core/CoreRuntimeComponent.cs index f2a6b7b6bc..a87873dd51 100644 --- a/src/Umbraco.Core/CoreRuntimeComponent.cs +++ b/src/Umbraco.Core/CoreRuntimeComponent.cs @@ -110,6 +110,9 @@ namespace Umbraco.Core composition.Container.RegisterCollectionBuilder() .Append(factory => factory.GetInstance().GetTypes()); + composition.Container.Register(new PerContainerLifetime()); + composition.Container.Register(new PerContainerLifetime()); + composition.Container.RegisterSingleton(factory => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance()))); diff --git a/src/Umbraco.Core/Models/PreValueCollection.cs b/src/Umbraco.Core/Models/PreValueCollection.cs index bb0e71b3e8..f505892dca 100644 --- a/src/Umbraco.Core/Models/PreValueCollection.cs +++ b/src/Umbraco.Core/Models/PreValueCollection.cs @@ -5,7 +5,7 @@ using System.Linq; namespace Umbraco.Core.Models { /// - /// Represents the pre-value data for a DataType + /// Represents the preValues for a data type. /// /// /// Due to the legacy nature of the data that can be stored for pre-values, we have this class which encapsulates the 2 different @@ -17,84 +17,72 @@ namespace Umbraco.Core.Models public class PreValueCollection : IDeepCloneable { private IDictionary _preValuesAsDictionary; - private IEnumerable _preValuesAsArray; + private PreValue[] _preValuesAsArray; + + /// + /// Gets the collection as an array. + /// public IEnumerable PreValuesAsArray { - get - { - if (_preValuesAsArray == null) - { - throw new InvalidOperationException("The current pre-value collection is dictionary based, use the PreValuesAsDictionary property instead"); - } - return _preValuesAsArray; - } - set { _preValuesAsArray = value; } - } - - public IDictionary PreValuesAsDictionary - { - get - { - if (_preValuesAsDictionary == null) - { - throw new InvalidOperationException("The current pre-value collection is array based, use the PreValuesAsArray property instead"); - } - return _preValuesAsDictionary; - } - set { _preValuesAsDictionary = value; } + get => _preValuesAsArray + ?? throw new InvalidOperationException("The current preValue collection is dictionary based, use the PreValuesAsDictionary property instead."); + set => _preValuesAsArray = value.ToArray(); } /// - /// Check if it is a dictionary based collection + /// Gets the collection as a dictionary. /// - public bool IsDictionaryBased + public IDictionary PreValuesAsDictionary { - get { return _preValuesAsDictionary != null; } + get => _preValuesAsDictionary + ?? throw new InvalidOperationException("The current preValue collection is array based, use the PreValuesAsArray property instead."); + set => _preValuesAsDictionary = value; } + /// + /// Gets a value indicating whether the collection is dictionary-based. + /// + public bool IsDictionaryBased => _preValuesAsDictionary != null; + + /// + /// Initializes a new array-based instance of the class. + /// public PreValueCollection(IEnumerable preVals) { - _preValuesAsArray = preVals; + _preValuesAsArray = preVals.ToArray(); } + /// + /// Initializes a new dictionary-based instance of the class. + /// public PreValueCollection(IDictionary preVals) { _preValuesAsDictionary = preVals; } /// - /// Regardless of how the pre-values are stored this will return as a dictionary, it will convert an array based to a dictionary + /// Gets the collection as a dictionary, even if it is array-based. /// - /// public IDictionary FormatAsDictionary() { if (IsDictionaryBased) - { return PreValuesAsDictionary; - } - //it's an array so need to format it, the alias will just be an iteration - var result = new Dictionary(); - var asArray = PreValuesAsArray.ToArray(); - for (var i = 0; i < asArray.Length; i++) - { - result.Add(i.ToInvariantString(), asArray[i]); - } - return result; + var dictionary = new Dictionary(); + for (var i = 0; i < _preValuesAsArray.Length; i++) + dictionary[i.ToInvariantString()] = _preValuesAsArray[i]; + return dictionary; } public object DeepClone() { var clone = (PreValueCollection) MemberwiseClone(); - if (_preValuesAsArray != null) - { - clone._preValuesAsArray = _preValuesAsArray.Select(x => (PreValue)x.DeepClone()).ToArray(); - } - if (_preValuesAsDictionary != null) - { - clone._preValuesAsDictionary = _preValuesAsDictionary.ToDictionary(x => x.Key, x => (PreValue)x.Value.DeepClone()); - } + if (_preValuesAsArray != null) + clone._preValuesAsArray = _preValuesAsArray.Select(x => (PreValue) x.DeepClone()).ToArray(); + + if (_preValuesAsDictionary != null) + clone._preValuesAsDictionary = _preValuesAsDictionary.ToDictionary(x => x.Key, x => (PreValue) x.Value.DeepClone()); return clone; } diff --git a/src/Umbraco.Core/Models/PublishedContent/DataTypeConfigurationSource.cs b/src/Umbraco.Core/Models/PublishedContent/DataTypeConfigurationSource.cs new file mode 100644 index 0000000000..94a1499ff0 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/DataTypeConfigurationSource.cs @@ -0,0 +1,38 @@ +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides a default implementation. + /// + internal class DataTypeConfigurationSource : IDataTypeConfigurationSource + { + private readonly IDataTypeService _dataTypeService; + private readonly PropertyEditorCollection _propertyEditors; + + // fixme - we might want to have a way to load *all* configs in a fast way? + + /// + /// Initializes a new instance of the class. + /// + /// A data type service. + /// The collection of property editors. + public DataTypeConfigurationSource(IDataTypeService dataTypeService, PropertyEditorCollection propertyEditors) + { + _dataTypeService = dataTypeService; + _propertyEditors = propertyEditors; + } + + /// + public object GetDataTypeConfiguration(string editorAlias, int id) + { + // fixme - we need a more efficient dataTypeService way of getting these + // fixme - would be nice not to pass editorAlias but annoying for tests? + //var datatype = _dataTypeService.GetDataTypeDefinitionById(id); + var collection = _dataTypeService.GetPreValuesCollectionByDataTypeId(id); + var editor = _propertyEditors[editorAlias /*datatype.PropertyEditorAlias*/]; + return editor.MapDataTypeConfiguration(collection); + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/IDataTypeConfigurationSource.cs b/src/Umbraco.Core/Models/PublishedContent/IDataTypeConfigurationSource.cs new file mode 100644 index 0000000000..039dcbaae8 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/IDataTypeConfigurationSource.cs @@ -0,0 +1,17 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides data type configurations. + /// + public interface IDataTypeConfigurationSource + { + /// + /// Gets a data type configuration. + /// + /// The property editor alias. + /// The data type identifier. + /// The data type configuration, as a strongly typed object if the property editor + /// supports it, otherwise as a Dictionary{string, string}. + object GetDataTypeConfiguration(string editorAlias, int id); + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs new file mode 100644 index 0000000000..a24923400c --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs @@ -0,0 +1,41 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Creates published content types. + /// + public interface IPublishedContentTypeFactory + { + /// + /// Creates a published content type. + /// + /// The item type. + /// An content type. + /// A published content type corresponding to the item type and content type. + PublishedContentType CreateContentType(PublishedItemType itemType, IContentTypeComposition contentType); + // fixme could we derive itemType from contentType? + + /// + /// Creates a published property type. + /// + /// The published content type owning the property. + /// A property type. + /// A published property type corresponding to the property type and owned by the published content type. + PublishedPropertyType CreatePropertyType(PublishedContentType contentType, PropertyType propertyType); + + /// + /// Creates a published property type. + /// + /// A value indicating whether the property is created by Umbraco. + /// fixme xplain. + PublishedPropertyType CreatePropertyType(string propertyTypeAlias, int dataTypeId, string editorAlias, bool umbraco = false); + + /// + /// Creates a published data type. + /// + /// The data type identifier. + /// The data type editor alias. + /// A published data type with the specified properties. + /// Properties are assumed to be consistent and not checked. + PublishedDataType CreateDataType(int id, string editorAlias); + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index 2f646f5eaf..8407c7f4dc 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Composing; namespace Umbraco.Core.Models.PublishedContent { @@ -17,66 +18,81 @@ namespace Umbraco.Core.Models.PublishedContent // fixme - benchmark this! private readonly Dictionary _indexes = new Dictionary(); - // internal so it can be used by PublishedNoCache which does _not_ want to cache anything and so will never - // use the static cache getter PublishedContentType.GetPublishedContentType(alias) below - anything else - // should use it. - // fixme - not true anymore internal and all?! + // fixme used in legacy page.cs that should die internal PublishedContentType(IContentType contentType) - : this(PublishedItemType.Content, contentType) + : this(PublishedItemType.Content, contentType, new CurrentPublishedContentTypeFactory()) { } - internal PublishedContentType(IMediaType mediaType) - : this(PublishedItemType.Media, mediaType) - { } - - internal PublishedContentType(IMemberType memberType) - : this(PublishedItemType.Member, memberType) - { } - - internal PublishedContentType(PublishedItemType itemType, IContentTypeComposition contentType) + // fixme above and should die + private class CurrentPublishedContentTypeFactory : IPublishedContentTypeFactory + { + public PublishedContentType CreateContentType(PublishedItemType itemType, IContentTypeComposition contentType) + { + return new PublishedContentType(itemType, contentType, this); + } + + public PublishedPropertyType CreatePropertyType(PublishedContentType contentType, PropertyType propertyType) + { + return new PublishedPropertyType(contentType, propertyType, Current.PublishedModelFactory, Current.PropertyValueConverters, this); + } + + public PublishedPropertyType CreatePropertyType(string propertyTypeAlias, int dataTypeId, string editorAlias, bool umbraco = false) + { + return new PublishedPropertyType(propertyTypeAlias, dataTypeId, editorAlias, umbraco, Current.PublishedModelFactory, Current.PropertyValueConverters, this); + } + + public PublishedDataType CreateDataType(int id, string editorAlias) + { + return new PublishedDataType(id, editorAlias, new DataTypeConfigurationSource(Current.Services.DataTypeService, Current.PropertyEditors)); + } + } + + // this is the main and only true ctor + internal PublishedContentType(PublishedItemType itemType, IContentTypeComposition contentType, IPublishedContentTypeFactory factory) + : this(contentType.Id, contentType.Alias, itemType, contentType.CompositionAliases()) { - Id = contentType.Id; - Alias = contentType.Alias; - ItemType = itemType; - CompositionAliases = new HashSet(contentType.CompositionAliases(), StringComparer.InvariantCultureIgnoreCase); var propertyTypes = contentType.CompositionPropertyTypes - .Select(x => new PublishedPropertyType(this, x)); + .Select(x => factory.CreatePropertyType(this, x)); + if (itemType == PublishedItemType.Member) - propertyTypes = WithMemberProperties(propertyTypes, this); + propertyTypes = WithMemberProperties(this, propertyTypes, factory); _propertyTypes = propertyTypes.ToArray(); + InitializeIndexes(); } - // internal so it can be used for unit tests - internal PublishedContentType(int id, string alias, IEnumerable propertyTypes) - : this(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes) - { } - - // internal so it can be used for unit tests - internal PublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) - : this(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes) - { } - - // internal so it can be used for unit tests - internal PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, IEnumerable propertyTypes) + private PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases) { Id = id; Alias = alias; ItemType = itemType; CompositionAliases = new HashSet(compositionAliases, StringComparer.InvariantCultureIgnoreCase); - if (itemType == PublishedItemType.Member) - propertyTypes = WithMemberProperties(propertyTypes); - _propertyTypes = propertyTypes.ToArray(); - foreach (var propertyType in _propertyTypes) - propertyType.ContentType = this; - InitializeIndexes(); } - // create floating content type - ie does not match anything in the DB - internal PublishedContentType(string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) - : this(0, alias, compositionAliases, propertyTypes) + // internal so it can be used for unit tests + internal PublishedContentType(int id, string alias, IEnumerable propertyTypes, IPublishedContentTypeFactory factory) + : this(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, factory) { } + // internal so it can be used for unit tests + internal PublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes, IPublishedContentTypeFactory factory) + : this(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, factory) + { } + + private PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, IEnumerable propertyTypes, IPublishedContentTypeFactory factory) + : this (id, alias, itemType, compositionAliases) + { + var propertyTypesA = propertyTypes.ToArray(); + foreach (var propertyType in propertyTypesA) + propertyType.ContentType = this; + + if (itemType == PublishedItemType.Member) + propertyTypesA = WithMemberProperties(this, propertyTypesA, factory).ToArray(); + _propertyTypes = propertyTypesA; + + InitializeIndexes(); + } + private void InitializeIndexes() { for (var i = 0; i < _propertyTypes.Length; i++) @@ -105,8 +121,7 @@ namespace Umbraco.Core.Models.PublishedContent { "LastPasswordChangeDate", Tuple.Create(Constants.DataTypes.Datetime, Constants.PropertyEditors.DateTimeAlias) }, }; - private static IEnumerable WithMemberProperties(IEnumerable propertyTypes, - PublishedContentType contentType = null) + private static IEnumerable WithMemberProperties(PublishedContentType contentType, IEnumerable propertyTypes, IPublishedContentTypeFactory factory) { var aliases = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var propertyType in propertyTypes) @@ -117,8 +132,9 @@ namespace Umbraco.Core.Models.PublishedContent foreach (var propertyType in BuiltinMemberProperties .Where(kvp => aliases.Contains(kvp.Key) == false) - .Select(kvp => new PublishedPropertyType(kvp.Key, kvp.Value.Item1, kvp.Value.Item2, umbraco: true))) + .Select(kvp => factory.CreatePropertyType(kvp.Key, kvp.Value.Item1, kvp.Value.Item2, umbraco: true))) { + // fixme why would it be null? if (contentType != null) propertyType.ContentType = contentType; yield return propertyType; } @@ -144,8 +160,7 @@ namespace Umbraco.Core.Models.PublishedContent // this is the ONLY place where we compare ALIASES! public int GetPropertyIndex(string alias) { - int index; - if (_indexes.TryGetValue(alias, out index)) return index; // fastest + if (_indexes.TryGetValue(alias, out var index)) return index; // fastest if (_indexes.TryGetValue(alias.ToLowerInvariant(), out index)) return index; // slower return -1; } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeConverter.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeConverter.cs index e003926462..094f481db9 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeConverter.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeConverter.cs @@ -2,36 +2,27 @@ using System.ComponentModel; using System.Globalization; using System.Linq; -using Umbraco.Core.Composing; namespace Umbraco.Core.Models.PublishedContent { internal class PublishedContentTypeConverter : TypeConverter { - private static readonly Type[] ConvertableTypes = { typeof(int) }; + private static readonly Type[] ConvertingTypes = { typeof(int) }; public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { - return ConvertableTypes.Any(x => TypeHelper.IsTypeAssignableFrom(x, destinationType)) - || base.CanConvertFrom(context, destinationType); + return ConvertingTypes.Any(x => x.IsAssignableFrom(destinationType)) + || CanConvertFrom(context, destinationType); } - public override object ConvertTo( - ITypeDescriptorContext context, - CultureInfo culture, - object value, - Type destinationType) + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { - var publishedContent = value as IPublishedContent; - if (publishedContent == null) + if (!(value is IPublishedContent publishedContent)) return null; - if (TypeHelper.IsTypeAssignableFrom(destinationType)) - { - return publishedContent.Id; - } - - return base.ConvertTo(context, culture, value, destinationType); + return typeof(int).IsAssignableFrom(destinationType) + ? publishedContent.Id + : base.ConvertTo(context, culture, value, destinationType); } } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs new file mode 100644 index 0000000000..1394ba4387 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides a default implementation for . + /// + internal class PublishedContentTypeFactory : IPublishedContentTypeFactory + { + private readonly IPublishedModelFactory _publishedModelFactory; + private readonly PropertyValueConverterCollection _propertyValueConverters; + private readonly IDataTypeConfigurationSource _dataTypeConfigurationSource; + + public PublishedContentTypeFactory(IPublishedModelFactory publishedModelFactory, PropertyValueConverterCollection propertyValueConverters, IDataTypeConfigurationSource dataTypeConfigurationSource) + { + _publishedModelFactory = publishedModelFactory; + _propertyValueConverters = propertyValueConverters; + _dataTypeConfigurationSource = dataTypeConfigurationSource; + } + + /// + public PublishedContentType CreateContentType(PublishedItemType itemType, IContentTypeComposition contentType) + { + return new PublishedContentType(itemType, contentType, this); + } + + // for tests + internal PublishedContentType CreateContentType(int id, string alias, IEnumerable propertyTypes) + { + return new PublishedContentType(id, alias, propertyTypes, this); + } + + // for tests + internal PublishedContentType CreateContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) + { + return new PublishedContentType(id, alias, compositionAliases, propertyTypes, this); + } + + /// + public PublishedPropertyType CreatePropertyType(PublishedContentType contentType, PropertyType propertyType) + { + return new PublishedPropertyType(contentType, propertyType, _publishedModelFactory, _propertyValueConverters, this); + } + + /// + public PublishedPropertyType CreatePropertyType(string propertyTypeAlias, int dataTypeId, string editorAlias, bool umbraco = false) + { + return new PublishedPropertyType(propertyTypeAlias, dataTypeId, editorAlias, umbraco, _publishedModelFactory, _propertyValueConverters, this); + } + + /// + public PublishedDataType CreateDataType(int id, string editorAlias) + { + return new PublishedDataType(id, editorAlias, _dataTypeConfigurationSource); + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedDataType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedDataType.cs new file mode 100644 index 0000000000..3e0eb4633e --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedDataType.cs @@ -0,0 +1,62 @@ +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Represents a published data type. + /// + /// + /// Instances of the class are immutable, ie + /// if the data type changes, then a new class needs to be created. + /// These instances should be created by an . + /// + public class PublishedDataType + { + private readonly IDataTypeConfigurationSource _dataTypeConfigurationSource; + private object _configuration; + + /// + /// Initializes a new instance of the class. + /// + internal PublishedDataType(int id, string editorAlias, IDataTypeConfigurationSource dataTypeConfigurationSource) + { + _dataTypeConfigurationSource = dataTypeConfigurationSource ?? throw new ArgumentNullException(nameof(dataTypeConfigurationSource)); + + Id = id; + EditorAlias = editorAlias; + } + + /// + /// Gets the data type identifier. + /// + public int Id { get; } // definition id + + /// + /// Gets the data type editor alias. + /// + public string EditorAlias { get; } + + /// + /// Gets the data type configuration. + /// + public object Configuration + => _configuration ?? (_configuration = _dataTypeConfigurationSource.GetDataTypeConfiguration(EditorAlias, Id)); + + /// + /// Gets the data type configuration. + /// + /// The type of the configuration object. + /// The data type configuration. + public TConfiguration GetConfiguration() + where TConfiguration : class + { + var configuration = Configuration; + if (configuration == null) return default; + + if (!(configuration is TConfiguration asTConfiguration)) + throw new InvalidCastException($"Cannot cast data type configuration of type {configuration.GetType()} to {typeof(TConfiguration)}."); + + return asTConfiguration; + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs index 026d1ef1f8..81981bb6a8 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs @@ -1,19 +1,19 @@ using System; using System.Xml.Linq; using System.Xml.XPath; -using Umbraco.Core.Composing; using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Models.PublishedContent { /// - /// Represents an type. + /// Represents a published property type. /// /// Instances of the class are immutable, ie /// if the property type changes, then a new class needs to be created. public class PublishedPropertyType { - private readonly PropertyValueConverterCollection _converters; + private readonly IPublishedModelFactory _publishedModelFactory; + private readonly PropertyValueConverterCollection _propertyValueConverters; private readonly object _locker = new object(); private volatile bool _initialized; private IPropertyValueConverter _converter; @@ -24,68 +24,46 @@ namespace Umbraco.Core.Models.PublishedContent #region Constructors + // the first ctor is the default one, used in PublishedContentType to create the property types + // the second ctor is the test ctor, + // some parameters are optional, and they are all assumed to be valid and consistent + /// /// Initialize a new instance of the class within a , - /// with a . + /// and with a . /// - /// The published content type. - /// The property type. /// The new published property type belongs to the published content type and corresponds to the property type. - public PublishedPropertyType(PublishedContentType contentType, PropertyType propertyType) + public PublishedPropertyType(PublishedContentType contentType, PropertyType propertyType, IPublishedModelFactory publishedModelFactory, PropertyValueConverterCollection propertyValueConverters, IPublishedContentTypeFactory factory) + : this(propertyType.Alias, propertyType.DataTypeDefinitionId, propertyType.PropertyEditorAlias, false, publishedModelFactory, propertyValueConverters, factory) { // PropertyEditor [1:n] DataTypeDefinition [1:n] PropertyType - _converters = Current.PropertyValueConverters; // fixme really? - - ContentType = contentType; - PropertyTypeAlias = propertyType.Alias; - - DataTypeId = propertyType.DataTypeDefinitionId; - PropertyEditorAlias = propertyType.PropertyEditorAlias; + ContentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); } /// - /// Initializes a new instance of the class with a property type alias and a property editor alias. + /// Initializes a new instance of the class. /// - /// The property type alias. - /// The property editor alias. /// - /// The new published property type does not belong to a published content type. - /// It is based upon the property editor, but has no datatype definition. This will work as long - /// as the datatype definition is not required to process (eg to convert) the property values. For - /// example, this may not work if the related IPropertyValueConverter requires the datatype definition - /// to make decisions, fetch prevalues, etc. - /// The value of is assumed to be valid. + /// The new published property type does not belong to a published content type. fixme should they? + /// The values of parameters are assumed to be valid and consistent. + /// /// - internal PublishedPropertyType(string propertyTypeAlias, string propertyEditorAlias, PropertyValueConverterCollection converters = null) - : this(propertyTypeAlias, 0, propertyEditorAlias, converters) - { } - - /// - /// Initializes a new instance of the class with a property type alias, - /// a datatype definition identifier, and a property editor alias. - /// - /// The property type alias. - /// The datatype definition identifier. - /// The property editor alias. - /// A value indicating whether the property is an Umbraco-defined property. - /// - /// The new published property type does not belong to a published content type. - /// The values of and are - /// assumed to be valid and consistent. - /// - internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias, PropertyValueConverterCollection converters = null, bool umbraco = false) + internal PublishedPropertyType(string propertyTypeAlias, int dataTypeId, string editorAlias, bool umbraco, IPublishedModelFactory publishedModelFactory, PropertyValueConverterCollection propertyValueConverters, IPublishedContentTypeFactory factory) { // ContentType // - in unit tests, to be set by PublishedContentType when creating it - _converters = converters ?? Current.PropertyValueConverters; // fixme really? + _publishedModelFactory = publishedModelFactory ?? throw new ArgumentNullException(nameof(publishedModelFactory)); + _propertyValueConverters = propertyValueConverters ?? throw new ArgumentNullException(nameof(propertyValueConverters)); PropertyTypeAlias = propertyTypeAlias; - DataTypeId = dataTypeDefinitionId; - PropertyEditorAlias = propertyEditorAlias; + DataTypeId = dataTypeId; + PropertyEditorAlias = editorAlias; IsUmbraco = umbraco; + + DataType = factory.CreateDataType(DataTypeId, PropertyEditorAlias); } #endregion @@ -93,28 +71,33 @@ namespace Umbraco.Core.Models.PublishedContent #region Property type /// - /// Gets or sets the published content type containing the property type. + /// Gets the published content type containing the property type. /// // internally set by PublishedContentType constructor public PublishedContentType ContentType { get; internal set; } /// - /// Gets or sets the alias uniquely identifying the property type. + /// Gets the data type of the property. /// - public string PropertyTypeAlias { get; } + public PublishedDataType DataType { get; } /// - /// Gets or sets the identifier uniquely identifying the data type supporting the property type. + /// Gets the alias uniquely identifying the property type. /// - public int DataTypeId { get; } + public string PropertyTypeAlias { get; } // fixme - should be .ContentType.?? /// - /// Gets or sets the alias uniquely identifying the property editor for the property type. + /// Gets the identifier uniquely identifying the data type supporting the property type. /// - public string PropertyEditorAlias { get; } + public int DataTypeId { get; } // fixme - should be .DataType.Id /// - /// Gets or sets a value indicating whether the property is an Umbraco-defined property. + /// Gets the alias uniquely identifying the property editor for the property type. + /// + public string PropertyEditorAlias { get; } // fixme - should be .DataType.EditorAlias + + /// + /// Gets a value indicating whether the property is an Umbraco-defined property. /// internal bool IsUmbraco { get; } @@ -138,7 +121,7 @@ namespace Umbraco.Core.Models.PublishedContent _converter = null; var isdefault = false; - foreach (var converter in _converters) + foreach (var converter in _propertyValueConverters) { if (converter.IsConverter(this) == false) continue; @@ -146,20 +129,20 @@ namespace Umbraco.Core.Models.PublishedContent if (_converter == null) { _converter = converter; - isdefault = _converters.IsDefault(converter); + isdefault = _propertyValueConverters.IsDefault(converter); continue; } if (isdefault) { - if (_converters.IsDefault(converter)) + if (_propertyValueConverters.IsDefault(converter)) { // previous was default, and got another default - if (_converters.Shadows(_converter, converter)) + if (_propertyValueConverters.Shadows(_converter, converter)) { // previous shadows, ignore } - else if (_converters.Shadows(converter, _converter)) + else if (_propertyValueConverters.Shadows(converter, _converter)) { // shadows previous, replace _converter = converter; @@ -183,7 +166,7 @@ namespace Umbraco.Core.Models.PublishedContent } else { - if (_converters.IsDefault(converter)) + if (_propertyValueConverters.IsDefault(converter)) { // previous was non-default, ignore default } @@ -203,7 +186,9 @@ namespace Umbraco.Core.Models.PublishedContent _modelClrType = _converter == null ? typeof (object) : _converter.GetPropertyValueType(this); } - // gets the cache level + /// + /// Gets the property cache level. + /// public PropertyCacheLevel CacheLevel { get @@ -213,10 +198,13 @@ namespace Umbraco.Core.Models.PublishedContent } } - // converts the source value into the inter value - // uses converters, else falls back to dark (& performance-wise expensive) magic - // source: the property source value - // preview: whether we are previewing or not + /// + /// Converts the source value into the intermediate value. + /// + /// The published element owning the property. + /// The source value. + /// A value indicating whether content should be considered draft. + /// The intermediate value. public object ConvertSourceToInter(IPublishedElement owner, object source, bool preview) { if (!_initialized) Initialize(); @@ -227,10 +215,14 @@ namespace Umbraco.Core.Models.PublishedContent : source; } - // converts the inter value into the clr value - // uses converters, else returns the inter value - // inter: the property inter value - // preview: whether we are previewing or not + /// + /// Converts the intermediate value into the object value. + /// + /// The published element owning the property. + /// The reference cache level. + /// The intermediate value. + /// A value indicating whether content should be considered draft. + /// The object value. public object ConvertInterToObject(IPublishedElement owner, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { if (!_initialized) Initialize(); @@ -241,11 +233,17 @@ namespace Umbraco.Core.Models.PublishedContent : inter; } - // converts the inter value into the xpath value - // uses the converter else returns the inter value as a string - // if successful, returns either a string or an XPathNavigator - // inter: the property inter value - // preview: whether we are previewing or not + /// + /// Converts the intermediate value into the XPath value. + /// + /// The published element owning the property. + /// The reference cache level. + /// The intermediate value. + /// A value indicating whether content should be considered draft. + /// The XPath value. + /// + /// The XPath value can be either a string or an XPathNavigator. + /// public object ConvertInterToXPath(IPublishedElement owner, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { if (!_initialized) Initialize(); @@ -261,8 +259,13 @@ namespace Umbraco.Core.Models.PublishedContent return inter.ToString().Trim(); } - // gets the property CLR type - // may contain some ModelType types + /// + /// Gets the property model Clr type. + /// + /// + /// The model Clr type may be a type, or may contain types. + /// For the actual Clr type, see . + /// public Type ModelClrType { get @@ -272,15 +275,20 @@ namespace Umbraco.Core.Models.PublishedContent } } - // gets the property CLR type - // with mapped ModelType types (may throw) - // fixme - inject the factory? + /// + /// Gets the property Clr type. + /// + /// + /// Returns the actual Clr type which does not contain types. + /// Mapping from may throw if some instances + /// could not be mapped to actual Clr types. + /// public Type ClrType { get { if (!_initialized) Initialize(); - return _clrType ?? (_clrType = Current.PublishedModelFactory.MapModelType(_modelClrType)); + return _clrType ?? (_clrType = _publishedModelFactory.MapModelType(_modelClrType)); } } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs index ee81c79082..e77b86d86a 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs @@ -190,5 +190,30 @@ namespace Umbraco.Core.PropertyEditors { return $"Name: {Name}, Alias: {Alias}, IsParameterEditor: {IsParameterEditor}"; } + + /// + /// Maps a preValues collection to a strongly typed object. + /// + /// The preValues collection. + /// A strongly typed object. + /// + /// Each property editor can declare a DataTypeConfiguration inner class that represents + /// its preValues in a nice strongly typed way, and override this method to convert the preValues + /// collection. + /// Otherwise, by default, this method will convert to a Dictionary{string, string}. + /// fixme - is this temp? + /// + public virtual object MapDataTypeConfiguration(PreValueCollection preValues) + { + var asDictionary = preValues.IsDictionaryBased + ? preValues.PreValuesAsDictionary + : preValues.FormatAsDictionary(); + + var configuration = new Dictionary(); + foreach (var kvp in asDictionary) + configuration[kvp.Key] = kvp.Value.Value; + + return configuration; + } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 8cdea3809a..9aaf7d4c19 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -645,13 +645,18 @@ + + + + + diff --git a/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs index 8ce55b3f58..d59a15e299 100644 --- a/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs @@ -7,8 +7,10 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Horology; using BenchmarkDotNet.Jobs; +using Moq; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; using Umbraco.Web.PublishedCache.XmlPublishedCache; namespace Umbraco.Tests.Benchmarks @@ -305,9 +307,9 @@ namespace Umbraco.Tests.Benchmarks private static PublishedContentType GetPublishedContentType(PublishedItemType type, string alias) { - return new PublishedContentType(alias, new string[] {}, - new List(Enumerable.Range(0, 10).Select(x => new PublishedPropertyType("prop" + x, 0, "test")))); - + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), Mock.Of()); + return factory.CreateContentType(0, alias, new string[] {}, + new List(Enumerable.Range(0, 10).Select(x => factory.CreatePropertyType("prop" + x, 0, "test")))); } } } diff --git a/src/Umbraco.Tests/Facade/ConvertersTests.cs b/src/Umbraco.Tests/Facade/ConvertersTests.cs index 9c4656c22d..1fd4652a4b 100644 --- a/src/Umbraco.Tests/Facade/ConvertersTests.cs +++ b/src/Umbraco.Tests/Facade/ConvertersTests.cs @@ -25,10 +25,11 @@ namespace Umbraco.Tests.Facade { new SimpleConverter1(), }); + var contentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, Mock.Of()); - var elementType1 = new PublishedContentType(1000, "element1", new[] + var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", new[] { - new PublishedPropertyType("prop1", "editor1", converters), + contentTypeFactory.CreatePropertyType("prop1", 0, "editor1"), }); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); @@ -77,15 +78,16 @@ namespace Umbraco.Tests.Facade { new SimpleConverter2(facadeAccessor), }); + var contentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, Mock.Of()); - var elementType1 = new PublishedContentType(1000, "element1", new[] + var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", new[] { - new PublishedPropertyType("prop1", "editor2", converters), + contentTypeFactory.CreatePropertyType("prop1", 0, "editor2"), }); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); - var cntType1 = new PublishedContentType(1001, "cnt1", Array.Empty()); + var cntType1 = contentTypeFactory.CreateContentType(1001, "cnt1", Array.Empty()); var cnt1 = new FacadeTestObjects.TestPublishedContent(cntType1, 1234, Guid.NewGuid(), new Dictionary(), false); cacheContent[cnt1.Id] = cnt1; @@ -157,24 +159,27 @@ namespace Umbraco.Tests.Facade facadeAccessorMock.Setup(x => x.Facade).Returns(facadeMock.Object); Current.Container.Register(f => facadeAccessorMock.Object); - var elementType1 = new PublishedContentType(1000, "element1", new[] + var converters = Current.Container.GetInstance(); + var contentTypeFactory = new PublishedContentTypeFactory(factory, converters, Mock.Of()); + + var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", new[] { - new PublishedPropertyType("prop1", "editor1"), + contentTypeFactory.CreatePropertyType("prop1", 0, "editor1"), }); - var elementType2 = new PublishedContentType(1001, "element2", new[] + var elementType2 = contentTypeFactory.CreateContentType(1001, "element2", new[] { - new PublishedPropertyType("prop2", "editor2"), + contentTypeFactory.CreatePropertyType("prop2", 0, "editor2"), }); - var contentType1 = new PublishedContentType(1002, "content1", new[] + var contentType1 = contentTypeFactory.CreateContentType(1002, "content1", new[] { - new PublishedPropertyType("prop1", "editor1"), + contentTypeFactory.CreatePropertyType("prop1", 0, "editor1"), }); - var contentType2 = new PublishedContentType(1003, "content2", new[] + var contentType2 = contentTypeFactory.CreateContentType(1003, "content2", new[] { - new PublishedPropertyType("prop2", "editor2"), + contentTypeFactory.CreatePropertyType("prop2", 0, "editor2"), }); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); diff --git a/src/Umbraco.Tests/Facade/NestedContentTests.cs b/src/Umbraco.Tests/Facade/NestedContentTests.cs index 0397bfa4c7..32aad1155b 100644 --- a/src/Umbraco.Tests/Facade/NestedContentTests.cs +++ b/src/Umbraco.Tests/Facade/NestedContentTests.cs @@ -1,11 +1,11 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; @@ -15,6 +15,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web; using Umbraco.Web.Models; +using Umbraco.Web.PropertyEditors; using Umbraco.Web.PropertyEditors.ValueConverters; using Umbraco.Web.PublishedCache; @@ -34,15 +35,8 @@ namespace Umbraco.Tests.Facade var container = new ServiceContainer(); container.ConfigureUmbracoCore(); - // fixme - temp needed by NestedContentHelper to cache preValues - container.RegisterSingleton(f => CacheHelper.NoCache); - var dataTypeService = new Mock(); - // fixme - temp both needed by NestedContentHelper to read preValues - container.RegisterSingleton(); - container.RegisterSingleton(f => dataTypeService.Object); - // mocked dataservice returns nested content preValues dataTypeService .Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny())) @@ -67,9 +61,6 @@ namespace Umbraco.Tests.Facade var publishedModelFactory = new Mock(); - // fixme - temp needed by PublishedPropertyType - container.RegisterSingleton(f => publishedModelFactory.Object); - // mocked model factory returns model type var modelTypes = new Dictionary { @@ -89,6 +80,16 @@ namespace Umbraco.Tests.Facade return element; }); + // mocked model factory creates model lists + publishedModelFactory + .Setup(x => x.CreateModelList(It.IsAny())) + .Returns((string alias) => + { + return alias == "contentN1" + ? (IList) new List() + : (IList) new List(); + }); + var contentCache = new Mock(); var facade = new Mock(); @@ -111,13 +112,22 @@ namespace Umbraco.Tests.Facade new NestedContentManyValueConverter(facadeAccessor.Object, publishedModelFactory.Object, proflog), }); - var propertyType1 = new PublishedPropertyType("property1", 1, Constants.PropertyEditors.NestedContentAlias, converters); - var propertyType2 = new PublishedPropertyType("property2", 2, Constants.PropertyEditors.NestedContentAlias, converters); - var propertyTypeN1 = new PublishedPropertyType("propertyN1", Constants.PropertyEditors.TextboxAlias, converters); + PropertyEditorCollection editors = null; + editors = new PropertyEditorCollection(new PropertyEditor[] + { + new NestedContentPropertyEditor(Mock.Of(), new Lazy(() => editors)) + }); - var contentType1 = new PublishedContentType(1, "content1", new[] { propertyType1 }); - var contentType2 = new PublishedContentType(2, "content2", new[] { propertyType2 }); - var contentTypeN1 = new PublishedContentType(2, "contentN1", new[] { propertyTypeN1 }); + var source = new DataTypeConfigurationSource(dataTypeService.Object, editors); + var factory = new PublishedContentTypeFactory(publishedModelFactory.Object, converters, source); + + var propertyType1 = factory.CreatePropertyType("property1", 1, Constants.PropertyEditors.NestedContentAlias); + var propertyType2 = factory.CreatePropertyType("property2", 2, Constants.PropertyEditors.NestedContentAlias); + var propertyTypeN1 = factory.CreatePropertyType("propertyN1", 0, Constants.PropertyEditors.TextboxAlias); + + var contentType1 = factory.CreateContentType(1, "content1", new[] { propertyType1 }); + var contentType2 = factory.CreateContentType(2, "content2", new[] { propertyType2 }); + var contentTypeN1 = factory.CreateContentType(2, "contentN1", new[] { propertyTypeN1 }); // mocked content cache returns content types contentCache diff --git a/src/Umbraco.Tests/Facade/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Facade/PropertyCacheLevelTests.cs index 4f30986ef9..e6edb5247c 100644 --- a/src/Umbraco.Tests/Facade/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Facade/PropertyCacheLevelTests.cs @@ -27,9 +27,10 @@ namespace Umbraco.Tests.Facade converter, }); - var setType1 = new PublishedContentType(1000, "set1", new[] + var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, Mock.Of()); + var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", new[] { - new PublishedPropertyType("prop1", "editor1", converters), + publishedContentTypeFactory.CreatePropertyType("prop1", 0, "editor1"), }); // PublishedElementPropertyBase.GetCacheLevels: @@ -101,9 +102,10 @@ namespace Umbraco.Tests.Facade converter, }); - var setType1 = new PublishedContentType(1000, "set1", new[] + var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, Mock.Of()); + var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", new[] { - new PublishedPropertyType("prop1", "editor1", converters), + publishedContentTypeFactory.CreatePropertyType("prop1", 0, "editor1"), }); var snapshotCache = new DictionaryCacheProvider(); @@ -171,9 +173,10 @@ namespace Umbraco.Tests.Facade converter, }); - var setType1 = new PublishedContentType(1000, "set1", new[] + var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, Mock.Of()); + var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", new[] { - new PublishedPropertyType("prop1", "editor1", converters), + publishedContentTypeFactory.CreatePropertyType("prop1", 0, "editor1"), }); Assert.Throws(() => diff --git a/src/Umbraco.Tests/Misc/LibraryTests.cs b/src/Umbraco.Tests/Misc/LibraryTests.cs index f75b8ae519..bcdfc30c03 100644 --- a/src/Umbraco.Tests/Misc/LibraryTests.cs +++ b/src/Umbraco.Tests/Misc/LibraryTests.cs @@ -11,6 +11,7 @@ using Umbraco.Tests.TestHelpers; using Umbraco.Web; using Umbraco.Web.PublishedCache.XmlPublishedCache; using LightInject; +using Moq; namespace Umbraco.Tests.Misc { @@ -25,6 +26,8 @@ namespace Umbraco.Tests.Misc { base.SetUp(); + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), Mock.Of()); + // need to specify a custom callback for unit tests // AutoPublishedContentTypes generates properties automatically // when they are requested, but we must declare those that we @@ -33,9 +36,9 @@ namespace Umbraco.Tests.Misc var propertyTypes = new[] { // AutoPublishedContentType will auto-generate other properties - new PublishedPropertyType("content", 0, "?"), + factory.CreatePropertyType("content", 0, "?"), }; - var type = new AutoPublishedContentType(0, "anything", propertyTypes); + var type = new AutoPublishedContentType(0, "anything", propertyTypes, factory); ContentTypesCache.GetPublishedContentTypeByAlias = (alias) => type; Debug.Print("INIT LIB {0}", ContentTypesCache.Get(PublishedItemType.Content, "anything") diff --git a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs index 96ab36d2c2..e465dbf84d 100644 --- a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System; +using System.Globalization; using System.Linq; using LightInject; using Moq; @@ -70,7 +71,8 @@ namespace Umbraco.Tests.PropertyEditors dataTypeService.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny())).Returns(new PreValueCollection(Enumerable.Empty())); var converter = new Umbraco.Web.PropertyEditors.ValueConverters.ImageCropperValueConverter(dataTypeService.Object); - var result = converter.ConvertSourceToInter(null, new PublishedPropertyType("test", 0, "test"), val1, false); // does not use type for conversion + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), Mock.Of()); + var result = converter.ConvertSourceToInter(null, factory.CreatePropertyType("test", 0, "test"), val1, false); // does not use type for conversion var resultShouldMatch = val2.SerializeToCropDataSet(); if (expected) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 521c1be2b9..a6d00e3935 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; using Umbraco.Web; @@ -119,6 +121,7 @@ namespace Umbraco.Tests.PublishedContent private IPublishedContent GetContent(bool createChildren, int indexVals) { + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), Mock.Of()); var contentTypeAlias = createChildren ? "Parent" : "Child"; var d = new TestPublishedContent { @@ -143,8 +146,8 @@ namespace Umbraco.Tests.PublishedContent }; d.Properties = new Collection(new List { - new RawValueProperty(new PublishedPropertyType("property1", ""), d, "value" + indexVals), - new RawValueProperty(new PublishedPropertyType("property2", ""), d, "value" + (indexVals + 1)) + new RawValueProperty(factory.CreatePropertyType("property1", 0, ""), d, "value" + indexVals), + new RawValueProperty(factory.CreatePropertyType("property2", 0, ""), d, "value" + (indexVals + 1)) }); if (createChildren) { @@ -160,12 +163,12 @@ namespace Umbraco.Tests.PublishedContent { //create additional columns, used to test the different columns for child nodes ((Collection) d.Properties).Add( - new RawValueProperty(new PublishedPropertyType("property4", ""), d, "value" + (indexVals + 2))); + new RawValueProperty(factory.CreatePropertyType("property4", 0, ""), d, "value" + (indexVals + 2))); } else { ((Collection) d.Properties).Add( - new RawValueProperty(new PublishedPropertyType("property3", ""), d, "value" + (indexVals + 2))); + new RawValueProperty(factory.CreatePropertyType("property3", 0, ""), d, "value" + (indexVals + 2))); } return d; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 8a5c86a549..e3785f3d80 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Collections.ObjectModel; using System.Web.Routing; using Moq; @@ -11,6 +12,7 @@ using Umbraco.Web.Security; using Umbraco.Core.Composing; using Current = Umbraco.Core.Composing.Current; using LightInject; +using Umbraco.Core.PropertyEditors; using Umbraco.Tests.Testing; namespace Umbraco.Tests.PublishedContent @@ -189,17 +191,18 @@ namespace Umbraco.Tests.PublishedContent private static SolidFacade CreateFacade() { + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), Mock.Of()); var caches = new SolidFacade(); var cache = caches.InnerContentCache; var props = new[] { - new PublishedPropertyType("prop1", 1, "?"), + factory.CreatePropertyType("prop1", 1, "?"), }; - var contentType1 = new PublishedContentType(1, "ContentType1", Enumerable.Empty(), props); - var contentType2 = new PublishedContentType(2, "ContentType2", Enumerable.Empty(), props); - var contentType2Sub = new PublishedContentType(3, "ContentType2Sub", Enumerable.Empty(), props); + var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); + var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), props); + var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), props); cache.Add(new SolidPublishedContent(contentType1) { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index cb75758e88..e818328fd8 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -4,8 +4,8 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Tests.TestHelpers; -using Umbraco.Web; using LightInject; +using Moq; namespace Umbraco.Tests.PublishedContent { @@ -14,23 +14,6 @@ namespace Umbraco.Tests.PublishedContent /// public abstract class PublishedContentTestBase : BaseWebTest { - public override void SetUp() - { - base.SetUp(); - - // need to specify a custom callback for unit tests - var propertyTypes = new[] - { - // AutoPublishedContentType will auto-generate other properties - new PublishedPropertyType("content", 0, Constants.PropertyEditors.TinyMCEAlias), - }; - var type = new AutoPublishedContentType(0, "anything", propertyTypes); - ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; - - var umbracoContext = GetUmbracoContext("/test"); - Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; - } - protected override void Compose() { base.Compose(); @@ -46,5 +29,25 @@ namespace Umbraco.Tests.PublishedContent .Append() .Append(); } + + protected override void Initialize() + { + base.Initialize(); + + var converters = Container.GetInstance(); + var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, Mock.Of()); + + // need to specify a custom callback for unit tests + var propertyTypes = new[] + { + // AutoPublishedContentType will auto-generate other properties + publishedContentTypeFactory.CreatePropertyType("content", 0, Constants.PropertyEditors.TinyMCEAlias), + }; + var type = new AutoPublishedContentType(0, "anything", propertyTypes, publishedContentTypeFactory); + ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; + + var umbracoContext = GetUmbracoContext("/test"); + Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; + } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs index 035510c3f0..9bcddf63d9 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Moq; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models.PublishedContent; @@ -313,14 +314,17 @@ namespace Umbraco.Tests.PublishedContent class AutoPublishedContentType : PublishedContentType { - private static readonly PublishedPropertyType Default = new PublishedPropertyType("*", 0, "?"); + private static readonly PublishedContentTypeFactory Factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), Mock.Of()); - public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) - : base(id, alias, Enumerable.Empty(), propertyTypes) + private static readonly PublishedPropertyType Default + = Factory.CreatePropertyType("*", 0, "?"); + + public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes, IPublishedContentTypeFactory publishedContentTypeFactory) + : base(id, alias, Enumerable.Empty(), propertyTypes, publishedContentTypeFactory) { } - public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) - : base(id, alias, compositionAliases, propertyTypes) + public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes, IPublishedContentTypeFactory publishedContentTypeFactory) + : base(id, alias, compositionAliases, propertyTypes, publishedContentTypeFactory) { } public override PublishedPropertyType GetPropertyType(string alias) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 99db5e6ec3..1582795dc7 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -9,7 +9,6 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Core.Composing; -using Current = Umbraco.Core.Composing.Current; using LightInject; using Umbraco.Tests.Testing; @@ -22,14 +21,19 @@ namespace Umbraco.Tests.PublishedContent [UmbracoTest(PluginManager = UmbracoTestOptions.PluginManager.PerFixture)] public class PublishedContentTests : PublishedContentTestBase { - private TypeLoader _typeLoader; - - public override void SetUp() + protected override void Compose() { - // required so we can access property.Value - //PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(); + base.Compose(); - base.SetUp(); + Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); + Container.RegisterSingleton(); + } + + protected override void Initialize() + { + base.Initialize(); + + var factory = Container.GetInstance() as PublishedContentTypeFactory; // need to specify a custom callback for unit tests // AutoPublishedContentTypes generates properties automatically @@ -37,26 +41,19 @@ namespace Umbraco.Tests.PublishedContent // explicitely want to be here... var propertyTypes = new[] - { - // AutoPublishedContentType will auto-generate other properties - new PublishedPropertyType("umbracoNaviHide", 0, Constants.PropertyEditors.TrueFalseAlias), - new PublishedPropertyType("selectedNodes", 0, "?"), - new PublishedPropertyType("umbracoUrlAlias", 0, "?"), - new PublishedPropertyType("content", 0, Constants.PropertyEditors.TinyMCEAlias), - new PublishedPropertyType("testRecursive", 0, "?"), - }; - var compositionAliases = new[] {"MyCompositionAlias"}; - var type = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes); + { + // AutoPublishedContentType will auto-generate other properties + factory.CreatePropertyType("umbracoNaviHide", 0, Constants.PropertyEditors.TrueFalseAlias), + factory.CreatePropertyType("selectedNodes", 0, "?"), + factory.CreatePropertyType("umbracoUrlAlias", 0, "?"), + factory.CreatePropertyType("content", 0, Constants.PropertyEditors.TinyMCEAlias), + factory.CreatePropertyType("testRecursive", 0, "?"), + }; + var compositionAliases = new[] { "MyCompositionAlias" }; + var type = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes, factory); ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; } - protected override void Compose() - { - base.Compose(); - - Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); - } - protected override TypeLoader CreatePluginManager(IServiceFactory f) { var pluginManager = base.CreatePluginManager(f); @@ -554,8 +551,10 @@ namespace Umbraco.Tests.PublishedContent [Test] public void FragmentProperty() { - var pt = new PublishedPropertyType("detached", Constants.PropertyEditors.IntegerAlias); - var ct = new PublishedContentType(0, "alias", new[] { pt }); + var factory = Container.GetInstance() as PublishedContentTypeFactory; + + var pt = factory.CreatePropertyType("detached", 0, Constants.PropertyEditors.IntegerAlias); + var ct = factory.CreateContentType(0, "alias", new[] { pt }); var prop = new PublishedElementPropertyBase(pt, null, false, PropertyCacheLevel.None, 5548); Assert.IsInstanceOf(prop.Value); Assert.AreEqual(5548, prop.Value); @@ -571,16 +570,18 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Fragment2() { - var pt1 = new PublishedPropertyType("legend", 0, Constants.PropertyEditors.TextboxAlias); - var pt2 = new PublishedPropertyType("image", 0, Constants.PropertyEditors.MediaPickerAlias); - var pt3 = new PublishedPropertyType("size", 0, Constants.PropertyEditors.IntegerAlias); + var factory = Container.GetInstance() as PublishedContentTypeFactory; + + var pt1 = factory.CreatePropertyType("legend", 0, Constants.PropertyEditors.TextboxAlias); + var pt2 = factory.CreatePropertyType("image", 0, Constants.PropertyEditors.MediaPickerAlias); + var pt3 = factory.CreatePropertyType("size", 0, Constants.PropertyEditors.IntegerAlias); const string val1 = "boom bam"; const int val2 = 0; const int val3 = 666; var guid = Guid.NewGuid(); - var ct = new PublishedContentType(0, "alias", new[] { pt1, pt2, pt3 }); + var ct = factory.CreateContentType(0, "alias", new[] { pt1, pt2, pt3 }); var c = new ImageWithLegendModel(ct, guid, new Dictionary { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 54c2b10fbc..10fb8bfc1e 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -130,11 +130,6 @@ namespace Umbraco.Tests.PublishedContent currSort = rootChildren[i].SortOrder; } } - - - - - } @@ -438,7 +433,6 @@ namespace Umbraco.Tests.PublishedContent new[] { mSubChild1.Id, mChild1.Id, mRoot.Id })); } - [Test] public void Convert_From_Standard_Xml() { @@ -490,6 +484,4 @@ namespace Umbraco.Tests.PublishedContent Assert.IsNull(converted); } } - - } diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index bc0c26c1cd..94f3004f7a 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -11,6 +11,8 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Sync; using Umbraco.Tests.TestHelpers; @@ -69,11 +71,14 @@ namespace Umbraco.Tests.Scoping var runtimeStateMock = new Mock(); runtimeStateMock.Setup(x => x.Level).Returns(() => RuntimeLevel.Run); + var contentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), Mock.Of()); + return new FacadeService( options, null, runtimeStateMock.Object, ServiceContext, + contentTypeFactory, UowProvider, facadeAccessor, Logger, diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index 73518156b5..a80c174b0f 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using LightInject; using Moq; @@ -6,6 +7,7 @@ using NUnit.Framework; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers.Stubs; @@ -16,13 +18,14 @@ namespace Umbraco.Tests.TestHelpers [TestFixture, RequiresSTA] public abstract class BaseWebTest : TestWithDatabaseBase { - public override void SetUp() + protected override void Initialize() { - base.SetUp(); + base.Initialize(); // need to specify a custom callback for unit tests // AutoPublishedContentTypes generates properties automatically - var type = new AutoPublishedContentType(0, "anything", new PublishedPropertyType[] {}); + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), Mock.Of()); + var type = new AutoPublishedContentType(0, "anything", new PublishedPropertyType[] { }, factory); ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; } diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 02f3e6fe06..49c9db2ca3 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -29,6 +29,7 @@ using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using LightInject; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Tests.TestHelpers { @@ -249,6 +250,7 @@ namespace Umbraco.Tests.TestHelpers Current.Services.ContentTypeService, Current.Services.MediaTypeService, Current.Services.MemberTypeService, + Container.GetInstance(), Current.Logger); // testing=true so XmlStore will not use the file nor the database @@ -256,6 +258,7 @@ namespace Umbraco.Tests.TestHelpers var facadeAccessor = new UmbracoContextFacadeAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor); var service = new FacadeService( Current.Services, + Container.GetInstance(), (ScopeProvider) Current.ScopeProvider, UowProvider, cache, facadeAccessor, Current.Logger, ContentTypesCache, null, true, Options.FacadeServiceRepositoryEvents); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 28640e1ac5..252230ae7a 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -182,6 +182,8 @@ namespace Umbraco.Tests.Testing .SetProducer(Enumerable.Empty); Container.RegisterCollectionBuilder(); + Container.RegisterSingleton(); + Container.RegisterSingleton(); } protected virtual void ComposeCacheHelper() diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index f1400c38b9..f71d6a7434 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Strings; @@ -423,7 +424,8 @@ namespace Umbraco.Tests.Web.Mvc //var provider = new ScopeUnitOfWorkProvider(databaseFactory, new RepositoryFactory(Mock.Of())); var scopeProvider = TestObjects.GetScopeProvider(Mock.Of()); var uowProvider = TestObjects.GetScopeUnitOfWorkProvider(Mock.Of(), scopeProvider: scopeProvider); - _service = new FacadeService(svcCtx, scopeProvider, uowProvider, cache, Enumerable.Empty(), null, Current.Logger, null, true, false); // no events + var factory = Mock.Of(); + _service = new FacadeService(svcCtx, factory, scopeProvider, uowProvider, cache, Enumerable.Empty(), null, Current.Logger, null, true, false); // no events var http = GetHttpContextFactory(url, routeData).HttpContext; diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index bebbaa7abe..1eaba364a5 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -57,7 +57,6 @@ namespace Umbraco.Web.Cache dataTypeCache.Result.ClearCacheByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_" + payload.Id); _idkMap.ClearCache(payload.Id); - NestedContentHelper.ClearCache(payload.Id); // fixme refactor nested content } // fixme - not sure I like these? diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentHelper.cs b/src/Umbraco.Web/PropertyEditors/NestedContentHelper.cs deleted file mode 100644 index 6d487ec209..0000000000 --- a/src/Umbraco.Web/PropertyEditors/NestedContentHelper.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Newtonsoft.Json.Linq; -using Umbraco.Core.Composing; -using Umbraco.Core.Models; - -namespace Umbraco.Web.PropertyEditors -{ - internal static class NestedContentHelper - { - private const string CacheKeyPrefix = "Umbraco.Web.PropertyEditors.NestedContent.GetPreValuesCollectionByDataTypeId_"; - - public static PreValueCollection GetPreValuesCollectionByDataTypeId(int dtdId) - { - var preValueCollection = (PreValueCollection) Current.ApplicationCache.RuntimeCache.GetCacheItem( - string.Concat(CacheKeyPrefix, dtdId), - () => Current.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(dtdId)); - - return preValueCollection; - } - - public static void ClearCache(int id) - { - Current.ApplicationCache.RuntimeCache.ClearCacheItem( - string.Concat(CacheKeyPrefix, id)); - } - - public static IContentType GetElementType(JObject item) - { - var contentTypeAlias = item[NestedContentPropertyEditor.ContentTypeAliasPropertyKey]?.ToObject(); - return string.IsNullOrEmpty(contentTypeAlias) - ? null - : Current.Services.ContentTypeService.Get(contentTypeAlias); - } - } -} diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs index 0a443b6888..b35a63705c 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs @@ -48,6 +48,14 @@ namespace Umbraco.Web.PropertyEditors // has to be lazy else circular dep in ctor private PropertyEditorCollection PropertyEditors => _propertyEditors.Value; + private static IContentType GetElementType(JObject item) + { + var contentTypeAlias = item[ContentTypeAliasPropertyKey]?.ToObject(); + return string.IsNullOrEmpty(contentTypeAlias) + ? null + : Current.Services.ContentTypeService.Get(contentTypeAlias); + } + #region Pre Value Editor protected override PreValueEditor CreatePreValueEditor() @@ -80,6 +88,34 @@ namespace Umbraco.Web.PropertyEditors #endregion + #region DataType Configuration + + public class DataTypeConfiguration + { + public string[] ContentTypes { get; set; } + public int? MinItems { get; set; } + public int? MaxItems { get; set; } + public bool ConfirmDeletes { get; set; } + public bool ShowIcons { get; set; } + public bool HideLabel { get; set; } + } + + public override object MapDataTypeConfiguration(PreValueCollection preValues) + { + var d = preValues.PreValuesAsDictionary; + return new DataTypeConfiguration + { + ContentTypes = d.TryGetValue("contentTypes", out var preValue) ? preValue.Value.Split(',') : Array.Empty(), + MinItems = d.TryGetValue("minItems", out preValue) && int.TryParse(preValue.Value, out var minItems) ? (int?) minItems : null, + MaxItems = d.TryGetValue("maxItems", out preValue) && int.TryParse(preValue.Value, out var maxItems) ? (int?) maxItems : null, + ConfirmDeletes = d.TryGetValue("confirmDeletes", out preValue) && preValue.Value == "1", + ShowIcons = d.TryGetValue("showIcons", out preValue) && preValue.Value == "1", + HideLabel = d.TryGetValue("hideLabel", out preValue) && preValue.Value == "1" + }; + } + + #endregion + #region Value Editor protected override PropertyValueEditor CreateValueEditor() @@ -134,7 +170,7 @@ namespace Umbraco.Web.PropertyEditors var o = value[i]; var propValues = ((JObject)o); - var contentType = NestedContentHelper.GetElementType(propValues); + var contentType = GetElementType(propValues); if (contentType == null) { continue; @@ -206,7 +242,7 @@ namespace Umbraco.Web.PropertyEditors var o = value[i]; var propValues = ((JObject)o); - var contentType = NestedContentHelper.GetElementType(propValues); + var contentType = GetElementType(propValues); if (contentType == null) { continue; @@ -284,7 +320,7 @@ namespace Umbraco.Web.PropertyEditors var o = value[i]; var propValues = ((JObject)o); - var contentType = NestedContentHelper.GetElementType(propValues); + var contentType = GetElementType(propValues); if (contentType == null) { continue; @@ -354,7 +390,7 @@ namespace Umbraco.Web.PropertyEditors var o = value[i]; var propValues = (JObject) o; - var contentType = NestedContentHelper.GetElementType(propValues); + var contentType = GetElementType(propValues); if (contentType == null) { continue; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs index 441b3bc61e..fabca03e98 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -34,11 +34,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// public override Type GetPropertyValueType(PublishedPropertyType propertyType) { - var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId); - var contentTypes = preValueCollection.PreValuesAsDictionary["contentTypes"].Value; - return contentTypes.Contains(",") + var contentTypes = propertyType.DataType.GetConfiguration().ContentTypes; + return contentTypes.Length > 1 ? typeof (IEnumerable) - : typeof (IEnumerable<>).MakeGenericType(ModelType.For(contentTypes)); + : typeof (IEnumerable<>).MakeGenericType(ModelType.For(contentTypes[0])); } /// @@ -63,12 +62,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (objects.Count == 0) return Enumerable.Empty(); - // fixme do NOT do it here! + use the facade cache - var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId); - var contentTypes = preValueCollection.PreValuesAsDictionary["contentTypes"].Value; - var elements = contentTypes.Contains(",") + var contentTypes = propertyType.DataType.GetConfiguration().ContentTypes; + var elements = contentTypes.Length > 1 ? new List() - : PublishedModelFactory.CreateModelList(contentTypes); + : PublishedModelFactory.CreateModelList(contentTypes[0]); foreach (var sourceObject in objects) { diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs index d90a6e52ad..c0ad9fb519 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs @@ -33,11 +33,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// public override Type GetPropertyValueType(PublishedPropertyType propertyType) { - var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId); - var contentTypes = preValueCollection.PreValuesAsDictionary["contentTypes"].Value; - return contentTypes.Contains(",") + var contentTypes = propertyType.DataType.GetConfiguration().ContentTypes; + return contentTypes.Length > 1 ? typeof(IPublishedElement) - : ModelType.For(contentTypes); + : ModelType.For(contentTypes[0]); } /// diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs index 09004be990..8b7bde46e3 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs @@ -30,14 +30,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (!IsNested(publishedProperty)) return false; - // fixme - the facade should provide this - var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(publishedProperty.DataTypeId); - var preValueDictionary = preValueCollection.PreValuesAsDictionary; - - return preValueDictionary.TryGetValue("minItems", out var minItems) - && preValueDictionary.TryGetValue("maxItems", out var maxItems) - && int.TryParse(minItems.Value, out var minItemsValue) && minItemsValue == 1 - && int.TryParse(maxItems.Value, out var maxItemsValue) && maxItemsValue == 1; + var config = publishedProperty.DataType.GetConfiguration(); + return config.MinItems == 1 && config.MaxItems == 1; } public static bool IsNestedMany(PublishedPropertyType publishedProperty) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs b/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs index ee27cd8f0b..c86572e47c 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs @@ -18,6 +18,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; @@ -33,6 +34,7 @@ namespace Umbraco.Web.PublishedCache.NuCache class FacadeService : FacadeServiceBase { private readonly ServiceContext _serviceContext; + private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; private readonly IScopeProvider _scopeProvider; private readonly IScopeUnitOfWorkProvider _uowProvider; private readonly Database _dataSource; @@ -73,13 +75,16 @@ namespace Umbraco.Web.PublishedCache.NuCache //private static int _singletonCheck; - public FacadeService(Options options, MainDom mainDom, IRuntimeState runtime, ServiceContext serviceContext, IScopeUnitOfWorkProvider uowProvider, IFacadeAccessor facadeAccessor, ILogger logger, IScopeProvider scopeProvider) + public FacadeService(Options options, MainDom mainDom, IRuntimeState runtime, + ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, + IScopeUnitOfWorkProvider uowProvider, IFacadeAccessor facadeAccessor, ILogger logger, IScopeProvider scopeProvider) : base(facadeAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) // throw new Exception("Singleton must be instancianted only once!"); _serviceContext = serviceContext; + _publishedContentTypeFactory = publishedContentTypeFactory; _uowProvider = uowProvider; _dataSource = new Database(); _logger = logger; @@ -270,7 +275,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // content (and types) are read-locked var contentTypes = _serviceContext.ContentTypeService.GetAll() - .Select(x => new PublishedContentType(PublishedItemType.Content, x)); + .Select(x => _publishedContentTypeFactory.CreateContentType(PublishedItemType.Content, x)); _contentStore.UpdateContentTypes(null, contentTypes, null); _localContentDb?.Clear(); @@ -286,7 +291,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private void LoadContentFromLocalDbLocked(IScopeUnitOfWork uow) { var contentTypes = _serviceContext.ContentTypeService.GetAll() - .Select(x => new PublishedContentType(PublishedItemType.Content, x)); + .Select(x => _publishedContentTypeFactory.CreateContentType(PublishedItemType.Content, x)); _contentStore.UpdateContentTypes(null, contentTypes, null); _logger.Debug("Loading content from local db..."); @@ -338,7 +343,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // locks & notes: see content var mediaTypes = _serviceContext.MediaTypeService.GetAll() - .Select(x => new PublishedContentType(PublishedItemType.Media, x)); + .Select(x => _publishedContentTypeFactory.CreateContentType(PublishedItemType.Media, x)); _mediaStore.UpdateContentTypes(null, mediaTypes, null); _localMediaDb?.Clear(); @@ -354,7 +359,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private void LoadMediaFromLocalDbLocked(IScopeUnitOfWork uow) { var mediaTypes = _serviceContext.MediaTypeService.GetAll() - .Select(x => new PublishedContentType(PublishedItemType.Media, x)); + .Select(x => _publishedContentTypeFactory.CreateContentType(PublishedItemType.Media, x)); _mediaStore.UpdateContentTypes(null, mediaTypes, null); _logger.Debug("Loading media from local db..."); @@ -815,8 +820,12 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content Types - private IEnumerable CreateContentTypes(PublishedItemType itemType, params int[] ids) + private IEnumerable CreateContentTypes(PublishedItemType itemType, int[] ids) { + // XxxTypeService.GetAll(empty) returns everything! + if (ids.Length == 0) + return Enumerable.Empty(); + IEnumerable contentTypes; switch (itemType) { @@ -835,7 +844,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // some may be missing - not checking here - return contentTypes.Select(x => new PublishedContentType(itemType, x)); + return contentTypes.Select(x => _publishedContentTypeFactory.CreateContentType(itemType, x)); } private PublishedContentType CreateContentType(PublishedItemType itemType, int id) @@ -856,7 +865,7 @@ namespace Umbraco.Web.PublishedCache.NuCache throw new ArgumentOutOfRangeException(nameof(itemType)); } - return contentType == null ? null : new PublishedContentType(itemType, contentType); + return contentType == null ? null : _publishedContentTypeFactory.CreateContentType(itemType, contentType); } private void RefreshContentTypesLocked(IEnumerable removedIds, IEnumerable refreshedIds, IEnumerable otherIds, IEnumerable newIds) @@ -981,7 +990,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var facadeCache = new StaticCacheProvider(); - var memberTypeCache = new PublishedContentTypeCache(null, null, _serviceContext.MemberTypeService, _logger); + var memberTypeCache = new PublishedContentTypeCache(null, null, _serviceContext.MemberTypeService, _publishedContentTypeFactory, _logger); var domainCache = new DomainCache(domainSnap); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs index a4cfeb1d04..59b14871bc 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs @@ -5,6 +5,7 @@ using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using LightInject; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.PublishedCache.NuCache { @@ -20,6 +21,7 @@ namespace Umbraco.Web.PublishedCache.NuCache factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), diff --git a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs b/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs index ff38a6a9ed..11243e4a7e 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -10,8 +9,10 @@ using Umbraco.Core.Services; namespace Umbraco.Web.PublishedCache { - // caches content, media and member types - + /// + /// Represents a content type cache. + /// + /// This cache is not snapshotted, so it refreshes any time things change. public class PublishedContentTypeCache { private readonly Dictionary _typesByAlias = new Dictionary(); @@ -19,51 +20,87 @@ namespace Umbraco.Web.PublishedCache private readonly IContentTypeService _contentTypeService; private readonly IMediaTypeService _mediaTypeService; private readonly IMemberTypeService _memberTypeService; - public readonly ILogger _logger; + private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; + private readonly ILogger _logger; private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); - internal PublishedContentTypeCache(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, ILogger logger) + // default ctor + internal PublishedContentTypeCache(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, IPublishedContentTypeFactory publishedContentTypeFactory, ILogger logger) { _contentTypeService = contentTypeService; _mediaTypeService = mediaTypeService; _memberTypeService = memberTypeService; _logger = logger; + _publishedContentTypeFactory = publishedContentTypeFactory; } // for unit tests ONLY - internal PublishedContentTypeCache(ILogger logger) + internal PublishedContentTypeCache(ILogger logger, IPublishedContentTypeFactory publishedContentTypeFactory) { _logger = logger; + _publishedContentTypeFactory = publishedContentTypeFactory; } + /// + /// Clears all cached content types. + /// public void ClearAll() { _logger.Debug("Clear all."); - using (new WriteLock(_lock)) + try { + _lock.EnterWriteLock(); + _typesByAlias.Clear(); _typesById.Clear(); } + finally + { + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); + } } + /// + /// Clears a cached content type. + /// + /// An identifier. public void ClearContentType(int id) { _logger.Debug("Clear content type w/id {0}.", () => id); - using (var l = new UpgradeableReadLock(_lock)) + try { - PublishedContentType type; - if (_typesById.TryGetValue(id, out type) == false) + _lock.EnterUpgradeableReadLock(); + + if (_typesById.TryGetValue(id, out var type) == false) return; - l.UpgradeToWriteLock(); + try + { + _lock.EnterWriteLock(); - _typesByAlias.Remove(GetAliasKey(type)); - _typesById.Remove(id); + _typesByAlias.Remove(GetAliasKey(type)); + _typesById.Remove(id); + } + finally + { + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); + } + } + finally + { + if (_lock.IsUpgradeableReadLockHeld) + _lock.ExitUpgradeableReadLock(); } } + /// + /// Clears all cached content types referencing a data type. + /// + /// A data type identifier. public void ClearDataType(int id) { _logger.Debug("Clear data type w/id {0}.", () => id); @@ -73,8 +110,10 @@ namespace Umbraco.Web.PublishedCache // IContentTypeComposition) and so every PublishedContentType having a property based upon // the cleared data type, be it local or inherited, will be cleared. - using (new WriteLock(_lock)) + try { + _lock.EnterWriteLock(); + var toRemove = _typesById.Values.Where(x => x.PropertyTypes.Any(xx => xx.DataTypeId == id)).ToArray(); foreach (var type in toRemove) { @@ -82,32 +121,84 @@ namespace Umbraco.Web.PublishedCache _typesById.Remove(type.Id); } } - } - - public PublishedContentType Get(PublishedItemType itemType, string alias) - { - var aliasKey = GetAliasKey(itemType, alias); - using (var l = new UpgradeableReadLock(_lock)) + finally { - PublishedContentType type; - if (_typesByAlias.TryGetValue(aliasKey, out type)) - return type; - type = CreatePublishedContentType(itemType, alias); - l.UpgradeToWriteLock(); - return _typesByAlias[aliasKey] = _typesById[type.Id] = type; + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); } } + /// + /// Gets a published content type. + /// + /// An item type. + /// An alias. + /// The published content type corresponding to the item type and alias. + public PublishedContentType Get(PublishedItemType itemType, string alias) + { + var aliasKey = GetAliasKey(itemType, alias); + + try + { + _lock.EnterUpgradeableReadLock(); + + if (_typesByAlias.TryGetValue(aliasKey, out var type)) + return type; + + type = CreatePublishedContentType(itemType, alias); + + try + { + _lock.EnterWriteLock(); + + return _typesByAlias[aliasKey] = _typesById[type.Id] = type; + } + finally + { + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); + } + } + finally + { + if (_lock.IsUpgradeableReadLockHeld) + _lock.ExitUpgradeableReadLock(); + } + } + + /// + /// Gets a published content type. + /// + /// An item type. + /// An identifier. + /// The published content type corresponding to the item type and identifier. public PublishedContentType Get(PublishedItemType itemType, int id) { - using (var l = new UpgradeableReadLock(_lock)) + try { - PublishedContentType type; - if (_typesById.TryGetValue(id, out type)) + _lock.EnterUpgradeableReadLock(); + + if (_typesById.TryGetValue(id, out var type)) return type; + type = CreatePublishedContentType(itemType, id); - l.UpgradeToWriteLock(); - return _typesByAlias[GetAliasKey(type)] = _typesById[type.Id] = type; + + try + { + _lock.EnterWriteLock(); + + return _typesByAlias[GetAliasKey(type)] = _typesById[type.Id] = type; + } + finally + { + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); + } + } + finally + { + if (_lock.IsUpgradeableReadLockHeld) + _lock.ExitUpgradeableReadLock(); } } @@ -135,7 +226,7 @@ namespace Umbraco.Web.PublishedCache if (contentType == null) throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with alias \"{alias}\"."); - return new PublishedContentType(itemType, contentType); + return _publishedContentTypeFactory.CreateContentType(itemType, contentType); } private PublishedContentType CreatePublishedContentType(PublishedItemType itemType, int id) @@ -162,22 +253,29 @@ namespace Umbraco.Web.PublishedCache if (contentType == null) throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with id {id}."); - return new PublishedContentType(itemType, contentType); + return _publishedContentTypeFactory.CreateContentType(itemType, contentType); } // for unit tests - changing the callback must reset the cache obviously private Func _getPublishedContentTypeByAlias; internal Func GetPublishedContentTypeByAlias { - get { return _getPublishedContentTypeByAlias; } + get => _getPublishedContentTypeByAlias; set { - using (new WriteLock(_lock)) + try { + _lock.EnterWriteLock(); + _typesByAlias.Clear(); _typesById.Clear(); _getPublishedContentTypeByAlias = value; } + finally + { + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); + } } } @@ -185,15 +283,22 @@ namespace Umbraco.Web.PublishedCache private Func _getPublishedContentTypeById; internal Func GetPublishedContentTypeById { - get { return _getPublishedContentTypeById; } + get => _getPublishedContentTypeById; set { - using (new WriteLock(_lock)) + try { + _lock.EnterWriteLock(); + _typesByAlias.Clear(); _typesById.Clear(); _getPublishedContentTypeById = value; } + finally + { + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); + } } } @@ -201,13 +306,20 @@ namespace Umbraco.Web.PublishedCache { string k; - if (itemType == PublishedItemType.Content) - k = "c"; - else if (itemType == PublishedItemType.Media) - k = "m"; - else if (itemType == PublishedItemType.Member) - k = "m"; - else throw new ArgumentOutOfRangeException(nameof(itemType)); + switch (itemType) + { + case PublishedItemType.Content: + k = "c"; + break; + case PublishedItemType.Media: + k = "m"; + break; + case PublishedItemType.Member: + k = "m"; + break; + default: + throw new ArgumentOutOfRangeException(nameof(itemType)); + } return k + ":" + alias; } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/FacadeService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/FacadeService.cs index 4c398f156e..4826e215ea 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/FacadeService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/FacadeService.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Umbraco.Core; using Umbraco.Core.Cache; @@ -9,7 +8,6 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Strings; @@ -35,6 +33,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // used in WebBootManager + tests public FacadeService(ServiceContext serviceContext, + IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, IScopeUnitOfWorkProvider uowProvider, ICacheProvider requestCache, @@ -43,11 +42,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache ILogger logger, MainDom mainDom, bool testing = false, bool enableRepositoryEvents = true) - : this(serviceContext, scopeProvider, uowProvider, requestCache, segmentProviders, facadeAccessor, logger, null, mainDom, testing, enableRepositoryEvents) + : this(serviceContext, publishedContentTypeFactory, scopeProvider, uowProvider, requestCache, segmentProviders, facadeAccessor, logger, null, mainDom, testing, enableRepositoryEvents) { } // used in some tests internal FacadeService(ServiceContext serviceContext, + IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, IScopeUnitOfWorkProvider uowProvider, ICacheProvider requestCache, @@ -56,10 +56,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache PublishedContentTypeCache contentTypeCache, MainDom mainDom, bool testing, bool enableRepositoryEvents) - : this(serviceContext, scopeProvider, uowProvider, requestCache, Enumerable.Empty(), facadeAccessor, logger, contentTypeCache, mainDom, testing, enableRepositoryEvents) + : this(serviceContext, publishedContentTypeFactory, scopeProvider, uowProvider, requestCache, Enumerable.Empty(), facadeAccessor, logger, contentTypeCache, mainDom, testing, enableRepositoryEvents) { } private FacadeService(ServiceContext serviceContext, + IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, IScopeUnitOfWorkProvider uowProvider, ICacheProvider requestCache, @@ -73,7 +74,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { _routesCache = new RoutesCache(); _contentTypeCache = contentTypeCache - ?? new PublishedContentTypeCache(serviceContext.ContentTypeService, serviceContext.MediaTypeService, serviceContext.MemberTypeService, logger); + ?? new PublishedContentTypeCache(serviceContext.ContentTypeService, serviceContext.MediaTypeService, serviceContext.MemberTypeService, publishedContentTypeFactory, logger); _xmlStore = new XmlStore(serviceContext, scopeProvider, uowProvider, _routesCache, _contentTypeCache, segmentProviders, facadeAccessor, mainDom, testing, enableRepositoryEvents); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs index c4a78ba9d5..b8c761eff8 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Scoping; using Umbraco.Web.HealthCheck.Checks.DataIntegrity; using LightInject; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.PublishedCache.XmlPublishedCache { @@ -21,6 +22,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // register the XML facade service composition.SetFacadeService(factory => new FacadeService( factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance().RequestCache, diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index fdd83b8aa4..dca465f8c0 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -243,7 +243,6 @@ -