diff --git a/src/Umbraco.Core/Collections/IReadOnlyKeyedCollection.cs b/src/Umbraco.Core/Collections/IReadOnlyKeyedCollection.cs
deleted file mode 100644
index 8d78241275..0000000000
--- a/src/Umbraco.Core/Collections/IReadOnlyKeyedCollection.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Collections.Generic;
-
-namespace Umbraco.Core.Collections
-{
- ///
- /// A readonly keyed collection
- ///
- ///
- public interface IReadOnlyKeyedCollection : IReadOnlyList
- {
- IEnumerable Keys { get; }
- bool TryGetValue(TKey key, out TVal val);
- TVal this[string key] { get; }
- bool Contains(TKey key);
- }
-}
diff --git a/src/Umbraco.Core/Collections/ObservableDictionary.cs b/src/Umbraco.Core/Collections/ObservableDictionary.cs
index caa2be92a8..ded87c30a6 100644
--- a/src/Umbraco.Core/Collections/ObservableDictionary.cs
+++ b/src/Umbraco.Core/Collections/ObservableDictionary.cs
@@ -14,20 +14,21 @@ namespace Umbraco.Core.Collections
///
/// The type of elements contained in the BindableCollection
/// The type of the indexing key
- public class ObservableDictionary : ObservableCollection
+ public class ObservableDictionary : ObservableCollection, IReadOnlyDictionary, IDictionary
{
- protected Dictionary Indecies = new Dictionary();
- protected Func KeySelector;
+ protected Dictionary Indecies { get; }
+ protected Func KeySelector { get; }
///
/// Create new ObservableDictionary
///
/// Selector function to create key from value
- public ObservableDictionary(Func keySelector)
+ public ObservableDictionary(Func keySelector, IEqualityComparer equalityComparer = null)
: base()
{
if (keySelector == null) throw new ArgumentException("keySelector");
KeySelector = keySelector;
+ Indecies = new Dictionary(equalityComparer);
}
#region Protected Methods
@@ -73,7 +74,7 @@ namespace Umbraco.Core.Collections
}
#endregion
- public virtual bool ContainsKey(TKey key)
+ public bool ContainsKey(TKey key)
{
return Indecies.ContainsKey(key);
}
@@ -83,7 +84,7 @@ namespace Umbraco.Core.Collections
///
/// Key of element to replace
///
- public virtual TValue this[TKey key]
+ public TValue this[TKey key]
{
get { return this[Indecies[key]]; }
@@ -112,7 +113,7 @@ namespace Umbraco.Core.Collections
///
///
/// False if key not found
- public virtual bool Replace(TKey key, TValue value)
+ public bool Replace(TKey key, TValue value)
{
if (!Indecies.ContainsKey(key)) return false;
//confirm key matches
@@ -124,7 +125,7 @@ namespace Umbraco.Core.Collections
}
- public virtual bool Remove(TKey key)
+ public bool Remove(TKey key)
{
if (!Indecies.ContainsKey(key)) return false;
@@ -138,7 +139,7 @@ namespace Umbraco.Core.Collections
///
///
///
- public virtual void ChangeKey(TKey currentKey, TKey newKey)
+ public void ChangeKey(TKey currentKey, TKey newKey)
{
if (!Indecies.ContainsKey(currentKey))
{
@@ -155,6 +156,75 @@ namespace Umbraco.Core.Collections
Indecies.Add(newKey, currentIndex);
}
+ #region IDictionary and IReadOnlyDictionary implementation
+
+ public bool TryGetValue(TKey key, out TValue val)
+ {
+ if (Indecies.TryGetValue(key, out var index))
+ {
+ val = this[index];
+ return true;
+ }
+ val = default;
+ return false;
+ }
+
+ ///
+ /// Returns all keys
+ ///
+ public IEnumerable Keys => Indecies.Keys;
+
+ ///
+ /// Returns all values
+ ///
+ public IEnumerable Values => base.Items;
+
+ ICollection IDictionary.Keys => Indecies.Keys;
+
+ //this will never be used
+ ICollection IDictionary.Values => Values.ToList();
+
+ bool ICollection>.IsReadOnly
+ {
+ get { return false; }
+ }
+
+ IEnumerator> IEnumerable>.GetEnumerator()
+ {
+ foreach (var i in Values)
+ {
+ var key = KeySelector(i);
+ yield return new KeyValuePair(key, i);
+ }
+ }
+
+ void IDictionary.Add(TKey key, TValue value)
+ {
+ Add(value);
+ }
+
+ void ICollection>.Add(KeyValuePair item)
+ {
+ Add(item.Value);
+ }
+
+ bool ICollection>.Contains(KeyValuePair item)
+ {
+ return ContainsKey(item.Key);
+ }
+
+ void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ throw new NotImplementedException();
+ }
+
+ bool ICollection>.Remove(KeyValuePair item)
+ {
+ return Remove(item.Key);
+ }
+
+ #endregion
+
internal class DuplicateKeyException : Exception
{
diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index abe28522ba..25a30da6db 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -89,7 +89,7 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo PublishedSelector = ExpressionHelper.GetPropertyInfo(x => x.Published);
public readonly PropertyInfo ReleaseDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ReleaseDate);
public readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ExpireDate);
- public readonly PropertyInfo PublishNamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.PublishNames);
+ public readonly PropertyInfo PublishNamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.PublishNames);
}
///
@@ -224,13 +224,13 @@ namespace Umbraco.Core.Models
public bool IsCulturePublished(string culture)
// just check _publishInfos
// a non-available culture could not become published anyways
- => _publishInfos != null && _publishInfos.Contains(culture);
+ => _publishInfos != null && _publishInfos.ContainsKey(culture);
///
public bool WasCulturePublished(string culture)
// just check _publishInfosOrig - a copy of _publishInfos
// a non-available culture could not become published anyways
- => _publishInfosOrig != null && _publishInfosOrig.Contains(culture);
+ => _publishInfosOrig != null && _publishInfosOrig.ContainsKey(culture);
// adjust dates to sync between version, cultures etc
// used by the repo when persisting
@@ -260,7 +260,7 @@ namespace Umbraco.Core.Models
///
[IgnoreDataMember]
- public IReadOnlyKeyedCollection PublishNames => _publishInfos ?? NoNames;
+ public IReadOnlyDictionary PublishNames => _publishInfos ?? NoNames;
///
public string GetPublishName(string culture)
@@ -508,6 +508,9 @@ namespace Umbraco.Core.Models
//turn off change tracking
clone.DisableChangeTracking();
+ //need to manually clone this since it's not settable
+ clone._contentType = (IContentType)ContentType.DeepClone();
+
//if culture infos exist then deal with event bindings
if (clone._publishInfos != null)
{
diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs
index 1c5ee18be6..a288f7fac9 100644
--- a/src/Umbraco.Core/Models/ContentBase.cs
+++ b/src/Umbraco.Core/Models/ContentBase.cs
@@ -70,7 +70,7 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo DefaultContentTypeIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeId);
public readonly PropertyInfo PropertyCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties);
public readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo(x => x.WriterId);
- public readonly PropertyInfo CultureNamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.CultureNames);
+ public readonly PropertyInfo CultureNamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.CultureNames);
}
protected void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e)
@@ -156,11 +156,11 @@ namespace Umbraco.Core.Models
///
public bool IsCultureAvailable(string culture)
- => _cultureInfos != null && _cultureInfos.Contains(culture);
+ => _cultureInfos != null && _cultureInfos.ContainsKey(culture);
///
[DataMember]
- public virtual IReadOnlyKeyedCollection CultureNames => _cultureInfos ?? NoNames;
+ public virtual IReadOnlyDictionary CultureNames => _cultureInfos ?? NoNames;
///
public string GetCultureName(string culture)
@@ -373,7 +373,7 @@ namespace Umbraco.Core.Models
foreach (var (otherCulture, otherName) in other.CultureNames)
{
if (culture == "*" || culture == otherCulture)
- SetCultureName(otherName, otherCulture);
+ SetCultureName(otherName.Name, otherCulture);
}
}
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index 2a57420e9e..0a86f4cf09 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Models
private bool _allowedAsRoot; // note: only one that's not 'pure element type'
private bool _isContainer;
private PropertyGroupCollection _propertyGroups;
- private PropertyTypeCollection _propertyTypes;
+ private PropertyTypeCollection _noGroupPropertyTypes;
private IEnumerable _allowedContentTypes;
private bool _hasPropertyTypeBeenRemoved;
private ContentVariation _variations;
@@ -43,8 +43,8 @@ namespace Umbraco.Core.Models
// actually OK as IsPublishing is constant
// ReSharper disable once VirtualMemberCallInConstructor
- _propertyTypes = new PropertyTypeCollection(IsPublishing);
- _propertyTypes.CollectionChanged += PropertyTypesChanged;
+ _noGroupPropertyTypes = new PropertyTypeCollection(IsPublishing);
+ _noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged;
_variations = ContentVariation.Nothing;
}
@@ -64,8 +64,8 @@ namespace Umbraco.Core.Models
// actually OK as IsPublishing is constant
// ReSharper disable once VirtualMemberCallInConstructor
- _propertyTypes = new PropertyTypeCollection(IsPublishing);
- _propertyTypes.CollectionChanged += PropertyTypesChanged;
+ _noGroupPropertyTypes = new PropertyTypeCollection(IsPublishing);
+ _noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged;
_variations = ContentVariation.Nothing;
}
@@ -248,7 +248,7 @@ namespace Umbraco.Core.Models
{
get
{
- return _propertyTypes.Union(PropertyGroups.SelectMany(x => x.PropertyTypes));
+ return _noGroupPropertyTypes.Union(PropertyGroups.SelectMany(x => x.PropertyTypes));
}
}
@@ -261,12 +261,12 @@ namespace Umbraco.Core.Models
[DoNotClone]
public IEnumerable NoGroupPropertyTypes
{
- get => _propertyTypes;
+ get => _noGroupPropertyTypes;
set
{
- _propertyTypes = new PropertyTypeCollection(IsPublishing, value);
- _propertyTypes.CollectionChanged += PropertyTypesChanged;
- PropertyTypesChanged(_propertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ _noGroupPropertyTypes = new PropertyTypeCollection(IsPublishing, value);
+ _noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged;
+ PropertyTypesChanged(_noGroupPropertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
@@ -320,7 +320,7 @@ namespace Umbraco.Core.Models
{
if (PropertyTypeExists(propertyType.Alias) == false)
{
- _propertyTypes.Add(propertyType);
+ _noGroupPropertyTypes.Add(propertyType);
return true;
}
@@ -384,7 +384,7 @@ namespace Umbraco.Core.Models
}
//check through each local property type collection (not assigned to a tab)
- if (_propertyTypes.RemoveItem(propertyTypeAlias))
+ if (_noGroupPropertyTypes.RemoveItem(propertyTypeAlias))
{
if (!HasPropertyTypeBeenRemoved)
{
@@ -408,7 +408,7 @@ namespace Umbraco.Core.Models
foreach (var property in group.PropertyTypes)
{
property.PropertyGroupId = null;
- _propertyTypes.Add(property);
+ _noGroupPropertyTypes.Add(property);
}
// actually remove the group
@@ -421,7 +421,7 @@ namespace Umbraco.Core.Models
///
[IgnoreDataMember]
//fixme should we mark this as EditorBrowsable hidden since it really isn't ever used?
- internal PropertyTypeCollection PropertyTypeCollection => _propertyTypes;
+ internal PropertyTypeCollection PropertyTypeCollection => _noGroupPropertyTypes;
///
/// Indicates whether the current entity is dirty.
@@ -474,15 +474,15 @@ namespace Umbraco.Core.Models
//turn off change tracking
clone.DisableChangeTracking();
- if (clone._propertyTypes != null)
+ if (clone._noGroupPropertyTypes != null)
{
//need to manually wire up the event handlers for the property type collections - we've ensured
// its ignored from the auto-clone process because its return values are unions, not raw and
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
- clone._propertyTypes.CollectionChanged -= this.PropertyTypesChanged; //clear this event handler if any
- clone._propertyTypes = (PropertyTypeCollection)_propertyTypes.DeepClone(); //manually deep clone
- clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged; //re-assign correct event handler
+ clone._noGroupPropertyTypes.CollectionChanged -= this.PropertyTypesChanged; //clear this event handler if any
+ clone._noGroupPropertyTypes = (PropertyTypeCollection)_noGroupPropertyTypes.DeepClone(); //manually deep clone
+ clone._noGroupPropertyTypes.CollectionChanged += clone.PropertyTypesChanged; //re-assign correct event handler
}
if (clone._propertyGroups != null)
diff --git a/src/Umbraco.Core/Models/CultureNameCollection.cs b/src/Umbraco.Core/Models/CultureNameCollection.cs
index be6540c399..406abc7c0f 100644
--- a/src/Umbraco.Core/Models/CultureNameCollection.cs
+++ b/src/Umbraco.Core/Models/CultureNameCollection.cs
@@ -12,13 +12,14 @@ namespace Umbraco.Core.Models
///
/// The culture names of a content's variants
///
- public class CultureNameCollection : KeyedCollection, INotifyCollectionChanged, IDeepCloneable, IReadOnlyKeyedCollection
+ public class CultureNameCollection : ObservableDictionary, IDeepCloneable
{
///
/// Creates a new collection from another collection
///
///
- public CultureNameCollection(IEnumerable names) : base(StringComparer.InvariantCultureIgnoreCase)
+ public CultureNameCollection(IEnumerable names)
+ : base(x => x.Culture, StringComparer.InvariantCultureIgnoreCase)
{
foreach (var n in names)
Add(n);
@@ -27,21 +28,11 @@ namespace Umbraco.Core.Models
///
/// Creates a new collection
///
- public CultureNameCollection() : base(StringComparer.InvariantCultureIgnoreCase)
+ public CultureNameCollection()
+ : base(x => x.Culture, StringComparer.InvariantCultureIgnoreCase)
{
}
-
- ///
- /// Returns all keys in the collection
- ///
- public IEnumerable Keys => Dictionary != null ? Dictionary.Keys : this.Select(x => x.Culture);
-
- public bool TryGetValue(string culture, out CultureName name)
- {
- name = this.FirstOrDefault(x => x.Culture.InvariantEquals(culture));
- return name != null;
- }
-
+
///
/// Add or update the
///
@@ -63,78 +54,20 @@ namespace Umbraco.Core.Models
});
}
- ///
- /// Gets the index for a specified culture
- ///
- public int IndexOfKey(string key)
- {
- for (var i = 0; i < Count; i++)
- {
- if (this[i].Culture.InvariantEquals(key))
- return i;
- }
- return -1;
- }
-
- public event NotifyCollectionChangedEventHandler CollectionChanged;
-
- protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
- {
- CollectionChanged?.Invoke(this, args);
- }
-
public object DeepClone()
{
var clone = new CultureNameCollection();
foreach (var name in this)
{
- clone.Add((CultureName)name.DeepClone());
+ name.DisableChangeTracking();
+ var copy = (CultureName)name.DeepClone();
+ copy.ResetDirtyProperties(false);
+ clone.Add(copy);
+ name.EnableChangeTracking();
}
return clone;
}
-
- protected override string GetKeyForItem(CultureName item)
- {
- return item.Culture;
- }
-
- ///
- /// Resets the collection to only contain the instances referenced in the parameter.
- ///
- /// The property groups.
- ///
- internal void Reset(IEnumerable names)
- {
- Clear();
- foreach (var name in names)
- Add(name);
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
- }
-
- protected override void SetItem(int index, CultureName item)
- {
- base.SetItem(index, item);
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
- }
-
- protected override void RemoveItem(int index)
- {
- var removed = this[index];
- base.RemoveItem(index);
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
- }
-
- protected override void InsertItem(int index, CultureName item)
- {
- base.InsertItem(index, item);
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
- }
-
- protected override void ClearItems()
- {
- base.ClearItems();
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
- }
+
}
}
diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs
index 3ddffe8f75..125c1a0f55 100644
--- a/src/Umbraco.Core/Models/IContent.cs
+++ b/src/Umbraco.Core/Models/IContent.cs
@@ -133,7 +133,7 @@ namespace Umbraco.Core.Models
/// Because a dictionary key cannot be null this cannot get the invariant
/// name, which must be get via the property.
///
- IReadOnlyKeyedCollection PublishNames { get; }
+ IReadOnlyDictionary PublishNames { get; }
///
/// Gets the published cultures.
diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs
index 811cbf74f3..c84c768e9c 100644
--- a/src/Umbraco.Core/Models/IContentBase.cs
+++ b/src/Umbraco.Core/Models/IContentBase.cs
@@ -58,7 +58,7 @@ namespace Umbraco.Core.Models
/// Because a dictionary key cannot be null this cannot contain the invariant
/// culture name, which must be get or set via the property.
///
- IReadOnlyKeyedCollection CultureNames { get; }
+ IReadOnlyDictionary CultureNames { get; }
///
/// Gets the available cultures.
diff --git a/src/Umbraco.Core/Models/PropertyGroupCollection.cs b/src/Umbraco.Core/Models/PropertyGroupCollection.cs
index 80b663fa05..c5768c66db 100644
--- a/src/Umbraco.Core/Models/PropertyGroupCollection.cs
+++ b/src/Umbraco.Core/Models/PropertyGroupCollection.cs
@@ -14,10 +14,12 @@ namespace Umbraco.Core.Models
///
[Serializable]
[DataContract]
+ //TODO: Change this to ObservableDictionary so we can reduce the INotifyCollectionChanged implementation details
public class PropertyGroupCollection : KeyedCollection, INotifyCollectionChanged, IDeepCloneable
{
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
+ //fixme: this doesn't seem to be used anywhere
internal Action OnAdd;
internal PropertyGroupCollection()
diff --git a/src/Umbraco.Core/Models/PropertyTypeCollection.cs b/src/Umbraco.Core/Models/PropertyTypeCollection.cs
index 47710e04cb..6053a6a5bf 100644
--- a/src/Umbraco.Core/Models/PropertyTypeCollection.cs
+++ b/src/Umbraco.Core/Models/PropertyTypeCollection.cs
@@ -13,11 +13,13 @@ namespace Umbraco.Core.Models
///
[Serializable]
[DataContract]
+ //TODO: Change this to ObservableDictionary so we can reduce the INotifyCollectionChanged implementation details
public class PropertyTypeCollection : KeyedCollection, INotifyCollectionChanged, IDeepCloneable
{
[IgnoreDataMember]
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
+ //fixme: This doesn't seem to be used
[IgnoreDataMember]
internal Action OnAdd;
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
index 3f1ea3116e..bea6eb9bce 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
@@ -326,9 +326,11 @@ AND umbracoNode.id <> @id",
});
}
- // delete property types
- // ... by excepting entries from db with entries from collections
- if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(x => x.IsDirty()))
+ // Delete property types ... by excepting entries from db with entries from collections.
+ // We check if the entity's own PropertyTypes has been modified and then also check
+ // any of the property groups PropertyTypes has been modified.
+ // This specifically tells us if any property type collections have changed.
+ if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyGroups.Any(x => x.IsPropertyDirty("PropertyTypes")))
{
var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { entity.Id });
var dbPropertyTypeAlias = dbPropertyTypes.Select(x => x.Id);
@@ -338,10 +340,11 @@ AND umbracoNode.id <> @id",
DeletePropertyType(entity.Id, item);
}
- // delete tabs
- // ... by excepting entries from db with entries from collections
+ // Delete tabs ... by excepting entries from db with entries from collections.
+ // We check if the entity's own PropertyGroups has been modified.
+ // This specifically tells us if the property group collections have changed.
List orphanPropertyTypeIds = null;
- if (entity.IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty()))
+ if (entity.IsPropertyDirty("PropertyGroups"))
{
// todo
// we used to try to propagate tabs renaming downstream, relying on ParentId, but
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs
index 5cbc987ad0..1659ca6427 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs
@@ -359,7 +359,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// names also impact 'edited'
foreach (var (culture, name) in content.CultureNames)
- if (name != content.GetPublishName(culture))
+ if (name.Name != content.GetPublishName(culture))
(editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(culture);
// insert content variations
@@ -521,7 +521,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// names also impact 'edited'
foreach (var (culture, name) in content.CultureNames)
- if (name != content.GetPublishName(culture))
+ if (name.Name != content.GetPublishName(culture))
{
edited = true;
(editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(culture);
@@ -1120,7 +1120,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
VersionId = content.VersionId,
LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."),
Culture = culture,
- Name = name,
+ Name = name.Name,
UpdateDate = content.GetUpdateDate(culture) ?? DateTime.MinValue // we *know* there is a value
};
@@ -1135,7 +1135,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
VersionId = content.PublishedVersionId,
LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."),
Culture = culture,
- Name = name,
+ Name = name.Name,
UpdateDate = content.GetPublishDate(culture) ?? DateTime.MinValue // we *know* there is a value
};
}
@@ -1210,7 +1210,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var defaultCulture = LanguageRepository.GetDefaultIsoCode();
content.Name = defaultCulture != null && content.CultureNames.TryGetValue(defaultCulture, out var cultureName)
? cultureName.Name
- : content.CultureNames[0].Name;
+ : content.CultureNames.First().Value.Name;
}
else
{
@@ -1265,13 +1265,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// get a unique name
var otherNames = cultureNames.Select(x => new SimilarNodeName { Id = x.Id, Name = x.Name });
- var uniqueName = SimilarNodeName.GetUniqueName(otherNames, 0, name);
+ var uniqueName = SimilarNodeName.GetUniqueName(otherNames, 0, name.Name);
if (uniqueName == content.GetCultureName(culture)) continue;
// update the name, and the publish name if published
content.SetCultureName(uniqueName, culture);
- if (publishing && content.PublishNames.Contains(culture))
+ if (publishing && content.PublishNames.ContainsKey(culture))
content.SetPublishInfo(culture, uniqueName, DateTime.Now);
}
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MacroRepository.cs
index 546be0b4a8..594f26fa72 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/MacroRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MacroRepository.cs
@@ -160,7 +160,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
//update the properties if they've changed
var macro = (Macro)entity;
- if (macro.IsPropertyDirty("Properties") || macro.Properties.Any(x => x.IsDirty()))
+ if (macro.IsPropertyDirty("Properties") || macro.Properties.Values.Any(x => x.IsDirty()))
{
var ids = dto.MacroPropertyDtos.Where(x => x.Id > 0).Select(x => x.Id).ToArray();
if (ids.Length > 0)
@@ -173,7 +173,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var aliases = new Dictionary();
foreach (var propDto in dto.MacroPropertyDtos)
{
- var prop = macro.Properties.FirstOrDefault(x => x.Id == propDto.Id);
+ var prop = macro.Properties.Values.FirstOrDefault(x => x.Id == propDto.Id);
if (prop == null) throw new Exception("oops: property.");
if (propDto.Id == 0 || prop.IsPropertyDirty("Alias"))
{
@@ -195,7 +195,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
else
{
// update
- var property = macro.Properties.FirstOrDefault(x => x.Id == propDto.Id);
+ var property = macro.Properties.Values.FirstOrDefault(x => x.Id == propDto.Id);
if (property == null) throw new Exception("oops: property.");
if (property.IsDirty())
Database.Update(propDto);
diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs
index 9deadfa5af..a4fb535f04 100644
--- a/src/Umbraco.Core/Services/Implement/ContentService.cs
+++ b/src/Umbraco.Core/Services/Implement/ContentService.cs
@@ -836,7 +836,7 @@ namespace Umbraco.Core.Services.Implement
//track the cultures that have changed
var culturesChanging = content.ContentType.VariesByCulture()
- ? string.Join(",", content.CultureNames.Where(x => x.IsDirty()).Select(x => x.Culture))
+ ? string.Join(",", content.CultureNames.Where(x => x.Value.IsDirty()).Select(x => x.Key))
: null;
//TODO: Currently there's no way to change track which variant properties have changed, we only have change
// tracking enabled on all values on the Property which doesn't allow us to know which variants have changed.
@@ -1069,7 +1069,7 @@ namespace Umbraco.Core.Services.Implement
}
else
{
- culturesChanging = string.Join(",", content.PublishNames.Where(x => x.IsDirty()).Select(x => x.Culture));
+ culturesChanging = string.Join(",", content.PublishNames.Where(x => x.Value.IsDirty()).Select(x => x.Key));
}
}
@@ -1852,7 +1852,7 @@ namespace Umbraco.Core.Services.Implement
//track the cultures changing for auditing
var culturesChanging = content.ContentType.VariesByCulture()
- ? string.Join(",", content.CultureNames.Where(x => x.IsDirty()).Select(x => x.Culture))
+ ? string.Join(",", content.CultureNames.Where(x => x.Value.IsDirty()).Select(x => x.Key))
: null;
//TODO: Currently there's no way to change track which variant properties have changed, we only have change
// tracking enabled on all values on the Property which doesn't allow us to know which variants have changed.
diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs
index c0e8f80337..fff865e097 100644
--- a/src/Umbraco.Core/Services/Implement/PackagingService.cs
+++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs
@@ -1318,7 +1318,7 @@ namespace Umbraco.Core.Services.Implement
sortOrder = int.Parse(sortOrderAttribute.Value);
}
- if (macro.Properties.Any(x => string.Equals(x.Alias, propertyAlias, StringComparison.OrdinalIgnoreCase))) continue;
+ if (macro.Properties.Values.Any(x => string.Equals(x.Alias, propertyAlias, StringComparison.OrdinalIgnoreCase))) continue;
macro.Properties.Add(new MacroProperty(propertyAlias, propertyName, sortOrder, editorAlias));
sortOrder++;
}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 8990b6d326..bc6c8fee54 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -109,7 +109,6 @@
-
diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs
index 9b23ec3d6b..a7b3d0f446 100644
--- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs
+++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs
@@ -272,7 +272,7 @@ AnotherContentFinder
public void Resolves_Actions()
{
var actions = _typeLoader.GetActions();
- Assert.AreEqual(34, actions.Count());
+ Assert.AreEqual(33, actions.Count());
}
[Test]
diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs
index 0e62c41f46..8a996f9782 100644
--- a/src/Umbraco.Tests/Models/VariationTests.cs
+++ b/src/Umbraco.Tests/Models/VariationTests.cs
@@ -237,9 +237,9 @@ namespace Umbraco.Tests.Models
// variant dictionary of names work
Assert.AreEqual(2, content.CultureNames.Count);
- Assert.IsTrue(content.CultureNames.Contains(langFr));
+ Assert.IsTrue(content.CultureNames.ContainsKey(langFr));
Assert.AreEqual("name-fr", content.CultureNames[langFr].Name);
- Assert.IsTrue(content.CultureNames.Contains(langUk));
+ Assert.IsTrue(content.CultureNames.ContainsKey(langUk));
Assert.AreEqual("name-uk", content.CultureNames[langUk].Name);
}
diff --git a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs
index a3b9035c8d..5ae25d629f 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs
@@ -175,7 +175,7 @@ namespace Umbraco.Tests.Persistence.Repositories
// Assert
Assert.That(macro.HasIdentity, Is.True);
Assert.That(macro.Id, Is.EqualTo(4));//With 3 existing entries the Id should be 4
- Assert.Greater(macro.Properties.Single().Id, 0);
+ Assert.Greater(macro.Properties.Values.Single().Id, 0);
}
}
@@ -268,15 +268,14 @@ namespace Umbraco.Tests.Persistence.Repositories
repository.Save(macro);
-
// Assert
- Assert.Greater(macro.Properties.First().Id, 0); //ensure id is returned
+ Assert.Greater(macro.Properties.Values.First().Id, 0); //ensure id is returned
var result = repository.Get(1);
- Assert.Greater(result.Properties.First().Id, 0);
- Assert.AreEqual(1, result.Properties.Count());
- Assert.AreEqual("new1", result.Properties.First().Alias);
- Assert.AreEqual("New1", result.Properties.First().Name);
- Assert.AreEqual(3, result.Properties.First().SortOrder);
+ Assert.Greater(result.Properties.Values.First().Id, 0);
+ Assert.AreEqual(1, result.Properties.Values.Count());
+ Assert.AreEqual("new1", result.Properties.Values.First().Alias);
+ Assert.AreEqual("New1", result.Properties.Values.First().Name);
+ Assert.AreEqual(3, result.Properties.Values.First().SortOrder);
}
}
@@ -298,10 +297,10 @@ namespace Umbraco.Tests.Persistence.Repositories
// Assert
var result = repository.Get(macro.Id);
- Assert.AreEqual(1, result.Properties.Count());
- Assert.AreEqual("blah1", result.Properties.First().Alias);
- Assert.AreEqual("New1", result.Properties.First().Name);
- Assert.AreEqual(4, result.Properties.First().SortOrder);
+ Assert.AreEqual(1, result.Properties.Values.Count());
+ Assert.AreEqual("blah1", result.Properties.Values.First().Alias);
+ Assert.AreEqual("New1", result.Properties.Values.First().Name);
+ Assert.AreEqual(4, result.Properties.Values.First().SortOrder);
}
}
@@ -325,7 +324,7 @@ namespace Umbraco.Tests.Persistence.Repositories
// Assert
result = repository.Get(macro.Id);
- Assert.AreEqual(0, result.Properties.Count());
+ Assert.AreEqual(0, result.Properties.Values.Count());
}
}
@@ -355,8 +354,8 @@ namespace Umbraco.Tests.Persistence.Repositories
// Assert
var result = repository.Get(macro.Id);
- Assert.AreEqual(1, result.Properties.Count());
- Assert.AreEqual("blah2", result.Properties.Single().Alias);
+ Assert.AreEqual(1, result.Properties.Values.Count());
+ Assert.AreEqual("blah2", result.Properties.Values.Single().Alias);
}
}
@@ -382,8 +381,8 @@ namespace Umbraco.Tests.Persistence.Repositories
// Assert
var result = repository.Get(1);
- Assert.AreEqual("new1", result.Properties.First().Alias);
- Assert.AreEqual("this is a new name", result.Properties.First().Name);
+ Assert.AreEqual("new1", result.Properties.Values.First().Alias);
+ Assert.AreEqual("this is a new name", result.Properties.Values.First().Name);
}
}
@@ -408,7 +407,7 @@ namespace Umbraco.Tests.Persistence.Repositories
// Assert
var result = repository.Get(1);
- Assert.AreEqual("newAlias", result.Properties.First().Alias);
+ Assert.AreEqual("newAlias", result.Properties.Values.First().Alias);
}
}
diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs
index d5660e708f..99aa9b788a 100644
--- a/src/Umbraco.Tests/Services/ContentServiceTests.cs
+++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs
@@ -1308,7 +1308,7 @@ namespace Umbraco.Tests.Services
var published = ServiceContext.ContentService.SavePublishing(content);
//audit log will only show that french was published
var lastLog = ServiceContext.AuditService.GetLogs(content.Id).Last();
- Assert.AreEqual($"Published culture fr-fr", lastLog.Comment);
+ Assert.AreEqual($"Published cultures: fr-fr", lastLog.Comment);
//re-get
content = ServiceContext.ContentService.GetById(content.Id);
@@ -1317,7 +1317,7 @@ namespace Umbraco.Tests.Services
published = ServiceContext.ContentService.SavePublishing(content);
//audit log will only show that english was published
lastLog = ServiceContext.AuditService.GetLogs(content.Id).Last();
- Assert.AreEqual($"Published culture en-uk", lastLog.Comment);
+ Assert.AreEqual($"Published cultures: en-uk", lastLog.Comment);
}
[Test]
diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs
index 767ffd4fc2..b33ff83c4a 100644
--- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs
+++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs
@@ -628,7 +628,7 @@ namespace Umbraco.Tests.Services.Importing
// Assert
Assert.That(macros.Any(), Is.True);
- Assert.That(macros.First().Properties.Any(), Is.True);
+ Assert.That(macros.First().Properties.Values.Any(), Is.True);
var allMacros = ServiceContext.MacroService.GetAll().ToList();
foreach (var macro in macros)
diff --git a/src/Umbraco.Tests/Services/MacroServiceTests.cs b/src/Umbraco.Tests/Services/MacroServiceTests.cs
index fa86f4baab..6539a37114 100644
--- a/src/Umbraco.Tests/Services/MacroServiceTests.cs
+++ b/src/Umbraco.Tests/Services/MacroServiceTests.cs
@@ -195,7 +195,7 @@ namespace Umbraco.Tests.Services
macro.Properties["blah1"].EditorAlias = "new";
macro.Properties.Remove("blah3");
- var allPropKeys = macro.Properties.Select(x => new { x.Alias, x.Key }).ToArray();
+ var allPropKeys = macro.Properties.Values.Select(x => new { x.Alias, x.Key }).ToArray();
macroService.Save(macro);
@@ -228,10 +228,10 @@ namespace Umbraco.Tests.Services
macroService.Save(macro);
var result1 = macroService.GetById(macro.Id);
- Assert.AreEqual(4, result1.Properties.Count());
+ Assert.AreEqual(4, result1.Properties.Values.Count());
//simulate clearing the sections
- foreach (var s in result1.Properties.ToArray())
+ foreach (var s in result1.Properties.Values.ToArray())
{
result1.Properties.Remove(s.Alias);
}
@@ -244,7 +244,7 @@ namespace Umbraco.Tests.Services
//re-get
result1 = macroService.GetById(result1.Id);
- Assert.AreEqual(2, result1.Properties.Count());
+ Assert.AreEqual(2, result1.Properties.Values.Count());
}
diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
index df21739c0b..f05e9525c2 100644
--- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
+++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
@@ -22,6 +22,7 @@ using Umbraco.Web.Security;
using Umbraco.Web.Templates;
using System.Linq;
using Umbraco.Core.Services;
+using Umbraco.Core.Configuration;
namespace Umbraco.Tests.Web
{
@@ -49,6 +50,8 @@ namespace Umbraco.Tests.Web
Umbraco.Web.Composing.Current.UmbracoContextAccessor = new TestUmbracoContextAccessor();
Udi.ResetUdiTypes();
+
+ UmbracoConfig.For.SetUmbracoSettings(SettingsForTests.GetDefaultUmbracoSettings());
}
[TearDown]
@@ -90,7 +93,7 @@ namespace Umbraco.Tests.Web
.Returns((UmbracoContext umbCtx, IPublishedContent content, UrlProviderMode mode, string culture, Uri url) => "/my-test-url");
var globalSettings = SettingsForTests.GenerateMockGlobalSettings();
-
+
var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing);
var publishedContent = Mock.Of();
Mock.Get(publishedContent).Setup(x => x.Id).Returns(1234);
diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Macros/EditMacro.aspx.cs b/src/Umbraco.Web.UI/Umbraco/developer/Macros/EditMacro.aspx.cs
index 96433be9cc..f2d2d5dcd3 100644
--- a/src/Umbraco.Web.UI/Umbraco/developer/Macros/EditMacro.aspx.cs
+++ b/src/Umbraco.Web.UI/Umbraco/developer/Macros/EditMacro.aspx.cs
@@ -94,7 +94,7 @@ namespace Umbraco.Web.UI.Umbraco.Developer.Macros
{
var macroPropertyId = (HtmlInputHidden)((Control)sender).Parent.FindControl("macroPropertyID");
- var property = _macro.Properties.Single(x => x.Id == int.Parse(macroPropertyId.Value));
+ var property = _macro.Properties.Values.Single(x => x.Id == int.Parse(macroPropertyId.Value));
_macro.Properties.Remove(property);
Services.MacroService.Save(_macro);
@@ -104,7 +104,7 @@ namespace Umbraco.Web.UI.Umbraco.Developer.Macros
public void MacroPropertyBind()
{
- macroProperties.DataSource = _macro.Properties.OrderBy(x => x.SortOrder);
+ macroProperties.DataSource = _macro.Properties.Values.OrderBy(x => x.SortOrder);
macroProperties.DataBind();
}
@@ -152,7 +152,7 @@ namespace Umbraco.Web.UI.Umbraco.Developer.Macros
_macro.Properties.Add(new MacroProperty(
macroPropertyAliasNew.Text.Trim(),
macroPropertyNameNew.Text.Trim(),
- _macro.Properties.Any() ? _macro.Properties.Max(x => x.SortOrder) + 1 : 0,
+ _macro.Properties.Values.Any() ? _macro.Properties.Values.Max(x => x.SortOrder) + 1 : 0,
macroPropertyTypeNew.SelectedValue));
Services.MacroService.Save(_macro);
@@ -246,7 +246,7 @@ namespace Umbraco.Web.UI.Umbraco.Developer.Macros
var macroElementSortOrder = (TextBox)item.FindControl("macroPropertySortOrder");
var macroElementType = (DropDownList)item.FindControl("macroPropertyType");
- var prop = _macro.Properties.Single(x => x.Id == int.Parse(macroPropertyId.Value));
+ var prop = _macro.Properties.Values.Single(x => x.Id == int.Parse(macroPropertyId.Value));
var sortOrder = 0;
int.TryParse(macroElementSortOrder.Text, out sortOrder);
diff --git a/src/Umbraco.Web/Models/Mapping/MacroMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MacroMapperProfile.cs
index 9977b1cfb1..7bede52021 100644
--- a/src/Umbraco.Web/Models/Mapping/MacroMapperProfile.cs
+++ b/src/Umbraco.Web/Models/Mapping/MacroMapperProfile.cs
@@ -26,7 +26,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dto => dto.AdditionalData, expression => expression.Ignore());
CreateMap>()
- .ConvertUsing(macro => macro.Properties.Select(Mapper.Map).ToList());
+ .ConvertUsing(macro => macro.Properties.Values.Select(Mapper.Map).ToList());
CreateMap()
.ForMember(x => x.View, expression => expression.Ignore())
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
index a9903669b9..355a4e7644 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
@@ -1199,7 +1199,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
foreach (var (culture, name) in names)
{
- cultureData[culture] = new CultureVariation { Name = name, Date = content.GetUpdateDate(culture) ?? DateTime.MinValue };
+ cultureData[culture] = new CultureVariation { Name = name.Name, Date = content.GetUpdateDate(culture) ?? DateTime.MinValue };
}
//the dictionary that will be serialized
diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs
index 85d7128629..709f0d719a 100644
--- a/src/Umbraco.Web/Security/WebSecurity.cs
+++ b/src/Umbraco.Web/Security/WebSecurity.cs
@@ -33,9 +33,9 @@ namespace Umbraco.Web.Security
public WebSecurity(HttpContextBase httpContext, IUserService userService, IGlobalSettings globalSettings)
{
- _httpContext = httpContext ?? throw new ArgumentNullException(nameof(httpContext));
- _userService = userService ?? throw new ArgumentNullException(nameof(userService));
- _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings));
+ _httpContext = httpContext;
+ _userService = userService;
+ _globalSettings = globalSettings;
}
///
diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs
index 91cf690996..ea1a563f9c 100644
--- a/src/Umbraco.Web/umbraco.presentation/page.cs
+++ b/src/Umbraco.Web/umbraco.presentation/page.cs
@@ -396,7 +396,7 @@ namespace umbraco
return _cultureInfos;
return _cultureInfos = _inner.PublishNames
- .ToDictionary(x => x.Culture, x => new PublishedCultureInfo(x.Culture, x.Name, x.Date));
+ .ToDictionary(x => x.Key, x => new PublishedCultureInfo(x.Key, x.Value.Name, x.Value.Date));
}
}