// initializes a published content property with a value public Property(IPublishedPropertyType propertyType, PublishedContent content, PropertyData[] sourceValues, IPublishedSnapshotAccessor publishedSnapshotAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) : base(propertyType, referenceCacheLevel) { if (sourceValues != null) { foreach (var sourceValue in sourceValues) { if (sourceValue.Culture == "" && sourceValue.Segment == "") { _sourceValue = sourceValue.Value; } else { if (_sourceValues == null) { _sourceValues = new Dictionary <CompositeStringStringKey, SourceInterValue>(); } _sourceValues[new CompositeStringStringKey(sourceValue.Culture, sourceValue.Segment)] = new SourceInterValue { Culture = sourceValue.Culture, Segment = sourceValue.Segment, SourceValue = sourceValue.Value }; } } } _contentUid = content.Key; _content = content; _isPreviewing = content.IsPreviewing; _isMember = content.ContentType.ItemType == PublishedItemType.Member; _publishedSnapshotAccessor = publishedSnapshotAccessor; _variations = propertyType.Variations; }
public static void ContextualizeVariation( this IVariationContextAccessor variationContextAccessor, ContentVariation variations, int contentId, ref string?culture, ref string?segment) => variationContextAccessor.ContextualizeVariation(variations, (int?)contentId, ref culture, ref segment);
private static void ContextualizeVariation( this IVariationContextAccessor variationContextAccessor, ContentVariation variations, int?contentId, ref string?culture, ref string?segment) { if (culture != null && segment != null) { return; } // use context values VariationContext?publishedVariationContext = variationContextAccessor?.VariationContext; if (culture == null) { culture = variations.VariesByCulture() ? publishedVariationContext?.Culture : string.Empty; } if (segment == null) { if (variations.VariesBySegment()) { segment = contentId == null ? publishedVariationContext?.Segment : publishedVariationContext?.GetSegment(contentId.Value); } else { segment = string.Empty; } } }
private PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable <string> compositionAliases, ContentVariation variations) { Id = id; Alias = alias; ItemType = itemType; CompositionAliases = new HashSet <string>(compositionAliases, StringComparer.InvariantCultureIgnoreCase); Variations = variations; }
/// <summary> /// Initializes a new instance of the <see cref="PropertyType"/> class. /// </summary> /// <remarks>Set <paramref name="forceValueStorageType"/> to true to force the value storage type. Values assigned to /// the property, eg from the underlying datatype, will be ignored.</remarks> internal PropertyType(string propertyEditorAlias, ValueStorageType valueStorageType, bool forceValueStorageType, string propertyTypeAlias = null) { _propertyEditorAlias = propertyEditorAlias; _valueStorageType = valueStorageType; _forceValueStorageType = forceValueStorageType; _alias = propertyTypeAlias == null ? null : SanitizeAlias(propertyTypeAlias); _variations = ContentVariation.Nothing; }
/// <summary> /// Asserts the result of <see cref="ContentVariationExtensions.ValidateVariation(ContentVariation, string, string, bool, bool, bool)"/> /// </summary> /// <param name="variation"></param> /// <param name="culture"></param> /// <param name="segment"></param> /// <param name="exactAndWildcards">Validate using Exact + Wildcards flags</param> /// <param name="nonExactAndNoWildcards">Validate using non Exact + no Wildcard flags</param> /// <param name="exactAndNoWildcards">Validate using Exact + no Wildcard flags</param> /// <param name="nonExactAndWildcards">Validate using non Exact + Wildcard flags</param> private static void Assert4B(ContentVariation variation, string culture, string segment, bool exactAndWildcards, bool nonExactAndNoWildcards, bool exactAndNoWildcards, bool nonExactAndWildcards) { Assert.AreEqual(exactAndWildcards, variation.ValidateVariation(culture, segment, true, true, false)); Assert.AreEqual(nonExactAndNoWildcards, variation.ValidateVariation(culture, segment, false, false, false)); Assert.AreEqual(exactAndNoWildcards, variation.ValidateVariation(culture, segment, true, false, false)); Assert.AreEqual(nonExactAndWildcards, variation.ValidateVariation(culture, segment, false, true, false)); }
/// <summary> /// Validates that a combination of culture and segment is valid for the variation. /// </summary> /// <param name="variation">The variation.</param> /// <param name="culture">The culture.</param> /// <param name="segment">The segment.</param> /// <param name="exact">A value indicating whether to perform exact validation.</param> /// <param name="wildcards">A value indicating whether to support wildcards.</param> /// <param name="throwIfInvalid">A value indicating whether to throw a <see cref="NotSupportedException"/> when the combination is invalid.</param> /// <returns>True if the combination is valid; otherwise false.</returns> /// <remarks> /// <para>When validation is exact, the combination must match the variation exactly. For instance, if the variation is Culture, then /// a culture is required. When validation is not strict, the combination must be equivalent, or more restrictive: if the variation is /// Culture, an invariant combination is ok.</para> /// <para>Basically, exact is for one content type, or one property type, and !exact is for "all property types" of one content type.</para> /// <para>Both <paramref name="culture"/> and <paramref name="segment"/> can be "*" to indicate "all of them".</para> /// </remarks> /// <exception cref="NotSupportedException">Occurs when the combination is invalid, and <paramref name="throwIfInvalid"/> is true.</exception> public static bool ValidateVariation(this ContentVariation variation, string culture, string segment, bool exact, bool wildcards, bool throwIfInvalid) { culture = culture.NullOrWhiteSpaceAsNull(); segment = segment.NullOrWhiteSpaceAsNull(); // if wildcards are disabled, do not allow "*" if (!wildcards && (culture == "*" || segment == "*")) { if (throwIfInvalid) { throw new NotSupportedException($"Variation wildcards are not supported."); } return(false); } if (variation.VariesByCulture()) { // varies by culture // in exact mode, the culture cannot be null if (exact && culture == null) { if (throwIfInvalid) { throw new NotSupportedException($"Culture may not be null because culture variation is enabled."); } return(false); } } else { // does not vary by culture // the culture cannot have a value // unless wildcards and it's "*" if (culture != null && !(wildcards && culture == "*")) { if (throwIfInvalid) { throw new NotSupportedException($"Culture \"{culture}\" is invalid because culture variation is disabled."); } return(false); } } // if it does not vary by segment // the segment cannot have a value // segment may always be null, even when the ContentVariation.Segment flag is set for this variation, // therefore the exact parameter is not used in segment validation. if (!variation.VariesBySegment() && segment != null && !(wildcards && segment == "*")) { if (throwIfInvalid) { throw new NotSupportedException($"Segment \"{segment}\" is invalid because segment variation is disabled."); } return(false); } return(true); }
/// <summary> /// Initializes a new instance of the <see cref="PropertyType"/> class. /// </summary> /// <remarks>Set <paramref name="forceValueStorageType"/> to true to force the value storage type. Values assigned to /// the property, eg from the underlying datatype, will be ignored.</remarks> public PropertyType(IShortStringHelper shortStringHelper, string propertyEditorAlias, ValueStorageType valueStorageType, bool forceValueStorageType, string propertyTypeAlias = null) { _shortStringHelper = shortStringHelper; _propertyEditorAlias = propertyEditorAlias; _valueStorageType = valueStorageType; _forceValueStorageType = forceValueStorageType; _alias = propertyTypeAlias == null ? null : SanitizeAlias(propertyTypeAlias); _variations = ContentVariation.Nothing; }
private PublishedContentType(Guid key, int id, string alias, PublishedItemType itemType, IEnumerable <string> compositionAliases, ContentVariation variations, bool isElement) { Key = key; Id = id; Alias = alias; ItemType = itemType; CompositionAliases = new HashSet <string>(compositionAliases, StringComparer.InvariantCultureIgnoreCase); Variations = variations; IsElement = isElement; }
/// <summary> /// This constructor is for tests and is not intended to be used directly from application code. /// </summary> /// <remarks> /// <para>Values are assumed to be consistent and are not checked.</para> /// <para>The new published property type does not belong to a published content type.</para> /// </remarks> public PublishedPropertyType(string propertyTypeAlias, int dataTypeId, bool isUserProperty, ContentVariation variations, PropertyValueConverterCollection propertyValueConverters, IPublishedModelFactory publishedModelFactory, IPublishedContentTypeFactory factory) { _publishedModelFactory = publishedModelFactory ?? throw new ArgumentNullException(nameof(publishedModelFactory)); _propertyValueConverters = propertyValueConverters ?? throw new ArgumentNullException(nameof(propertyValueConverters)); Alias = propertyTypeAlias; IsUserProperty = isUserProperty; Variations = variations; DataType = factory.GetDataType(dataTypeId); }
private PropertyType CreateAndAddTagsPropertyType(ContentType contentType, ContentVariation variations = ContentVariation.Nothing) { PropertyType propertyType = new PropertyTypeBuilder() .WithPropertyEditorAlias("test") .WithAlias("tags") .WithDataTypeId(1041) .WithVariations(variations) .Build(); contentType.PropertyGroups.First().PropertyTypes.Add(propertyType); contentType.Variations = variations; return(propertyType); }
// clone for previewing as draft a published content that is published and has no draft public Property(Property origin, PublishedContent content) : base(origin.PropertyType, origin.ReferenceCacheLevel) { _sourceValue = origin._sourceValue; _sourceValues = origin._sourceValues; _contentUid = origin._contentUid; _content = content; _isPreviewing = true; _isMember = origin._isMember; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; _variations = origin._variations; }
/// <summary> /// Validates that a combination of culture and segment is valid for the variation. /// </summary> /// <param name="variation">The variation.</param> /// <param name="culture">The culture.</param> /// <param name="segment">The segment.</param> /// <param name="exact">A value indicating whether to perform exact validation.</param> /// <param name="wildcards">A value indicating whether to support wildcards.</param> /// <param name="throwIfInvalid">A value indicating whether to throw a <see cref="NotSupportedException"/> when the combination is invalid.</param> /// <returns>True if the combination is valid; otherwise false.</returns> /// <remarks> /// <para>When validation is exact, the combination must match the variation exactly. For instance, if the variation is Culture, then /// a culture is required. When validation is not strict, the combination must be equivalent, or more restrictive: if the variation is /// Culture, an invariant combination is ok.</para> /// <para>Basically, exact is for one content type, or one property type, and !exact is for "all property types" of one content type.</para> /// <para>Both <paramref name="culture"/> and <paramref name="segment"/> can be "*" to indicate "all of them".</para> /// </remarks> /// <exception cref="NotSupportedException">Occurs when the combination is invalid, and <paramref name="throwIfInvalid"/> is true.</exception> public static bool ValidateVariation(this ContentVariation variation, string culture, string segment, bool exact, bool wildcards, bool throwIfInvalid) { culture = culture.NullOrWhiteSpaceAsNull(); segment = segment.NullOrWhiteSpaceAsNull(); bool Validate(bool variesBy, string value) { if (variesBy) { // varies by // in exact mode, the value cannot be null (but it can be a wildcard) // in !wildcards mode, the value cannot be a wildcard (but it can be null) if ((exact && value == null) || (!wildcards && value == "*")) { return(false); } } else { // does not vary by value // the value cannot have a value // unless wildcards and it's "*" if (value != null && (!wildcards || value != "*")) { return(false); } } return(true); } if (!Validate(variation.VariesByCulture(), culture)) { if (throwIfInvalid) { throw new NotSupportedException($"Culture value \"{culture ?? "<null>"}\" is invalid."); } return(false); } if (!Validate(variation.VariesBySegment(), segment)) { if (throwIfInvalid) { throw new NotSupportedException($"Segment value \"{segment ?? "<null>"}\" is invalid."); } return(false); } return(true); }
/// <summary> /// Initializes a new instance of the <see cref="PropertyType"/> class. /// </summary> public PropertyType(IDataType dataType) { if (dataType == null) { throw new ArgumentNullException(nameof(dataType)); } if (dataType.HasIdentity) { _dataTypeId = dataType.Id; } _propertyEditorAlias = dataType.EditorAlias; _valueStorageType = dataType.DatabaseType; _variations = ContentVariation.Nothing; }
protected ContentTypeBase(int parentId) { if (parentId == 0) { throw new ArgumentOutOfRangeException(nameof(parentId)); } ParentId = parentId; _allowedContentTypes = new List <ContentTypeSort>(); _propertyGroups = new PropertyGroupCollection(); // actually OK as IsPublishing is constant // ReSharper disable once VirtualMemberCallInConstructor _noGroupPropertyTypes = new PropertyTypeCollection(SupportsPublishing); _noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged; _variations = ContentVariation.Nothing; }
protected ContentTypeBase(IContentTypeBase parent, string alias) { if (parent == null) { throw new ArgumentNullException(nameof(parent)); } SetParent(parent); _alias = alias; _allowedContentTypes = new List <ContentTypeSort>(); _propertyGroups = new PropertyGroupCollection(); // actually OK as IsPublishing is constant // ReSharper disable once VirtualMemberCallInConstructor _noGroupPropertyTypes = new PropertyTypeCollection(SupportsPublishing); _noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged; _variations = ContentVariation.Nothing; }
/// <summary> /// Initializes a new instance of the <see cref="PropertyType"/> class. /// </summary> public PropertyType(IShortStringHelper shortStringHelper, IDataType dataType) { if (dataType == null) { throw new ArgumentNullException(nameof(dataType)); } _shortStringHelper = shortStringHelper; if (dataType.HasIdentity) { _dataTypeId = dataType.Id; } _propertyEditorAlias = dataType.EditorAlias; _valueStorageType = dataType.DatabaseType; _variations = ContentVariation.Nothing; _alias = string.Empty; _name = string.Empty; }
/// <summary> /// Asserts the result of /// <see cref="ContentVariationExtensions.ValidateVariation(ContentVariation, string, string, bool, bool, bool)" /> /// where expectedResult matches all combinations of Exact + Wildcard /// </summary> private static void Assert4A(ContentVariation variation, string culture, string segment, bool expectedResult) => Assert4B(variation, culture, segment, expectedResult, expectedResult, expectedResult, expectedResult);
/// <inheritdoc /> public PublishedPropertyType CreatePropertyType(PublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations = ContentVariation.Nothing) { return(new PublishedPropertyType(contentType, propertyTypeAlias, dataTypeId, true, variations, _propertyValueConverters, _publishedModelFactory, this)); }
private static PublishedPropertyType CreatePropertyType(string propertyEditorAlias, object dataTypeConfiguration, ContentVariation variation) { var uploadDataType = new PublishedDataType(1234, propertyEditorAlias, new Lazy <object>(() => dataTypeConfiguration)); var propertyValueConverters = new PropertyValueConverterCollection(new IPropertyValueConverter[] { new UploadPropertyConverter(), new ImageCropperValueConverter(Mock.Of <ILogger <ImageCropperValueConverter> >()), }); var publishedModelFactory = Mock.Of <IPublishedModelFactory>(); var publishedContentTypeFactory = new Mock <IPublishedContentTypeFactory>(); publishedContentTypeFactory.Setup(x => x.GetDataType(It.IsAny <int>())) .Returns(uploadDataType); return(new PublishedPropertyType("umbracoFile", 42, true, variation, propertyValueConverters, publishedModelFactory, publishedContentTypeFactory.Object)); }
/// <summary> /// This constructor is for tests and is not intended to be used directly from application code. /// </summary> /// <remarks> /// <para>Values are assumed to be consisted and are not checked.</para> /// <para>The new published property type belongs to the published content type.</para> /// </remarks> public PublishedPropertyType(PublishedContentType contentType, string propertyTypeAlias, int dataTypeId, bool isUserProperty, ContentVariation variations, PropertyValueConverterCollection propertyValueConverters, IPublishedModelFactory publishedModelFactory, IPublishedContentTypeFactory factory) : this(propertyTypeAlias, dataTypeId, isUserProperty, variations, propertyValueConverters, publishedModelFactory, factory) { ContentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); }
/// <summary> /// Determines whether a variation varies by segment. /// </summary> /// <remarks>And then it could also vary by culture.</remarks> public static bool VariesBySegment(this ContentVariation variation) => (variation & ContentVariation.Segment) > 0;
/// <summary> /// Determines whether a variation varies by culture and segment. /// </summary> public static bool VariesByCultureAndSegment(this ContentVariation variation) => (variation & ContentVariation.CultureAndSegment) == ContentVariation.CultureAndSegment;
/// <summary> /// Determines whether a variation is invariant. /// </summary> public static bool VariesByNothing(this ContentVariation variation) => variation == ContentVariation.Nothing;
/// <summary> /// Determines whether a variation varies by culture. /// </summary> /// <remarks>And then it could also vary by segment.</remarks> public static bool VariesByCulture(this ContentVariation variation) => (variation & ContentVariation.Culture) > 0;
/// <summary> /// Creates a collection of <see cref="PropertyDataDto"/> from a collection of <see cref="Property"/> /// </summary> /// <param name="contentVariation"> /// The <see cref="ContentVariation"/> of the entity containing the collection of <see cref="Property"/> /// </param> /// <param name="currentVersionId"></param> /// <param name="publishedVersionId"></param> /// <param name="properties">The properties to map</param> /// <param name="languageRepository"></param> /// <param name="edited">out parameter indicating that one or more properties have been edited</param> /// <param name="editedCultures"> /// Out parameter containing a collection of edited cultures when the contentVariation varies by culture. /// The value of this will be used to populate the edited cultures in the umbracoDocumentCultureVariation table. /// </param> /// <returns></returns> public static IEnumerable <PropertyDataDto> BuildDtos(ContentVariation contentVariation, int currentVersionId, int publishedVersionId, IEnumerable <IProperty> properties, ILanguageRepository languageRepository, out bool edited, out HashSet <string>?editedCultures) { var propertyDataDtos = new List <PropertyDataDto>(); edited = false; editedCultures = null; // don't allocate unless necessary string?defaultCulture = null; //don't allocate unless necessary var entityVariesByCulture = contentVariation.VariesByCulture(); // create dtos for each property values, but only for values that do actually exist // ie have a non-null value, everything else is just ignored and won't have a db row foreach (var property in properties) { if (property.PropertyType.SupportsPublishing) { //create the resulting hashset if it's not created and the entity varies by culture if (entityVariesByCulture && editedCultures == null) { editedCultures = new HashSet <string>(StringComparer.OrdinalIgnoreCase); } // publishing = deal with edit and published values foreach (var propertyValue in property.Values) { var isInvariantValue = propertyValue.Culture == null && propertyValue.Segment == null; var isCultureValue = propertyValue.Culture != null; var isSegmentValue = propertyValue.Segment != null; // deal with published value if ((propertyValue.PublishedValue != null || isSegmentValue) && publishedVersionId > 0) { propertyDataDtos.Add(BuildDto(publishedVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue?.Segment, propertyValue?.PublishedValue)); } // deal with edit value if (propertyValue?.EditedValue != null || isSegmentValue) { propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue?.Culture), propertyValue?.Segment, propertyValue?.EditedValue)); } // property.Values will contain ALL of it's values, both variant and invariant which will be populated if the // administrator has previously changed the property type to be variant vs invariant. // We need to check for this scenario here because otherwise the editedCultures and edited flags // will end up incorrectly set in the umbracoDocumentCultureVariation table so here we need to // only process edited cultures based on the current value type and how the property varies. // The above logic will still persist the currently saved property value for each culture in case the admin // decides to swap the property's variance again, in which case the edited flag will be recalculated. if (property.PropertyType.VariesByCulture() && isInvariantValue || !property.PropertyType.VariesByCulture() && isCultureValue) { continue; } // use explicit equals here, else object comparison fails at comparing eg strings var sameValues = propertyValue?.PublishedValue == null ? propertyValue?.EditedValue == null : propertyValue.PublishedValue.Equals(propertyValue.EditedValue); edited |= !sameValues; if (entityVariesByCulture && !sameValues) { if (isCultureValue && propertyValue?.Culture is not null) { editedCultures?.Add(propertyValue.Culture); // report culture as edited } else if (isInvariantValue) { // flag culture as edited if it contains an edited invariant property if (defaultCulture == null) { defaultCulture = languageRepository.GetDefaultIsoCode(); } editedCultures?.Add(defaultCulture); } } } } else { foreach (var propertyValue in property.Values) { // not publishing = only deal with edit values if (propertyValue.EditedValue != null) { propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.EditedValue)); } } edited = true; } } return(propertyDataDtos); }
// for tests internal PublishedContentType CreateContentType(int id, string alias, IEnumerable <string> compositionAliases, IEnumerable <PublishedPropertyType> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { return(new PublishedContentType(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement)); }
// for tests internal PublishedPropertyType CreatePropertyType(string propertyTypeAlias, int dataTypeId, bool umbraco = false, ContentVariation variations = ContentVariation.Nothing) { return(new PublishedPropertyType(propertyTypeAlias, dataTypeId, umbraco, variations, _propertyValueConverters, _publishedModelFactory, this)); }
/// <summary> /// This constructor is for tests and is not intended to be used directly from application code. /// </summary> /// <remarks> /// <para>Values are assumed to be consistent and are not checked.</para> /// </remarks> public PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable <string> compositionAliases, Func <IPublishedContentType, IEnumerable <IPublishedPropertyType> > propertyTypes, ContentVariation variations, bool isElement = false) : this(id, alias, itemType, compositionAliases, variations, isElement) { _propertyTypes = propertyTypes(this).ToArray(); InitializeIndexes(); }
public static void ContextualizeVariation(this IVariationContextAccessor variationContextAccessor, ContentVariation variations, ref string culture, ref string segment) => variationContextAccessor.ContextualizeVariation(variations, null, ref culture, ref segment);