Files
Umbraco-CMS/src/Umbraco.Core/Models/PropertyCollection.cs
Bjarke Berg fc054e6546 Merge remote-tracking branch 'origin/netcore/dev' into netcore/netcore
# Conflicts:
#	build/NuSpecs/UmbracoCms.Web.nuspec
#	src/SolutionInfo.cs
#	src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs
#	src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs
#	src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts
#	src/Umbraco.Tests.AcceptanceTest/package.json
#	src/Umbraco.Tests/TestHelpers/TestObjects.cs
#	src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs
#	src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs
#	src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs
#	src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
#	src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs
#	src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs
#	src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs
#	src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js
#	src/Umbraco.Web.UI.Client/src/installer/steps/database.html
#	src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less
#	src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less
#	src/Umbraco.Web.UI.Client/src/less/forms.less
#	src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html
#	src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html
#	src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html
#	src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-actions.html
#	src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js
#	src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html
#	src/Umbraco.Web.UI.Client/src/views/dictionary/edit.html
#	src/Umbraco.Web.UI.Client/src/views/dictionary/list.html
#	src/Umbraco.Web.UI.Client/src/views/logViewer/search.html
#	src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/mediaTypes/edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/memberTypes/edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/layoutconfig.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/userpicker/userpicker.controller.js
#	src/Umbraco.Web.UI.NetCore/umbraco/UmbracoBackOffice/Default.cshtml
#	src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml
#	src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml
#	src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml
#	src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml
#	src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml
#	src/Umbraco.Web.UI/config/umbracoSettings.config
#	src/Umbraco.Web/Editors/BackOfficeServerVariables.cs
#	src/Umbraco.Web/Editors/DictionaryController.cs
#	src/Umbraco.Web/Editors/LogController.cs
#	src/Umbraco.Web/Editors/MediaTypeController.cs
#	src/Umbraco.Web/Editors/MemberGroupController.cs
2020-09-09 19:13:37 +02:00

205 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a collection of property values.
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class PropertyCollection : KeyedCollection<string, IProperty>, IPropertyCollection
{
private readonly object _addLocker = new object();
/// <summary>
/// Initializes a new instance of the <see cref="PropertyCollection"/> class.
/// </summary>
public PropertyCollection()
: base(StringComparer.InvariantCultureIgnoreCase)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="PropertyCollection"/> class.
/// </summary>
public PropertyCollection(IEnumerable<IProperty> properties)
: this()
{
Reset(properties);
}
/// <summary>
/// Replaces all properties, whilst maintaining validation delegates.
/// </summary>
private void Reset(IEnumerable<IProperty> properties)
{
//collection events will be raised in each of these calls
Clear();
//collection events will be raised in each of these calls
foreach (var property in properties)
Add(property);
}
/// <summary>
/// Replaces the property at the specified index with the specified property.
/// </summary>
protected override void SetItem(int index, IProperty property)
{
var oldItem = index >= 0 ? this[index] : property;
base.SetItem(index, property);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, property, oldItem));
}
/// <summary>
/// Removes the property at the specified index.
/// </summary>
protected override void RemoveItem(int index)
{
var removed = this[index];
base.RemoveItem(index);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
}
/// <summary>
/// Inserts the specified property at the specified index.
/// </summary>
protected override void InsertItem(int index, IProperty property)
{
base.InsertItem(index, property);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property));
}
/// <summary>
/// Removes all properties.
/// </summary>
protected override void ClearItems()
{
base.ClearItems();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <inheritdoc />
public new void Add(IProperty property)
{
lock (_addLocker) // TODO: why are we locking here and not everywhere else?!
{
var key = GetKeyForItem(property);
if (key != null)
{
if (Contains(key))
{
// transfer id and values if ...
var existing = this[key];
if (property.Id == 0 && existing.Id != 0)
property.Id = existing.Id;
if (property.Values.Count == 0 && existing.Values.Count > 0)
property.Values = existing.Values.Select(x => x.Clone()).ToList();
// replace existing with property and return,
// SetItem invokes OnCollectionChanged (but not OnAdd)
SetItem(IndexOfKey(key), property);
return;
}
}
//collection events will be raised in InsertItem with Add
base.Add(property);
}
}
/// <summary>
/// Gets the index for a specified property alias.
/// </summary>
private int IndexOfKey(string key)
{
for (var i = 0; i < Count; i++)
{
if (this[i].Alias.InvariantEquals(key))
return i;
}
return -1;
}
protected override string GetKeyForItem(IProperty item)
{
return item.Alias;
}
/// <summary>
/// Gets the property with the specified PropertyType.
/// </summary>
internal IProperty this[IPropertyType propertyType]
{
get
{
return this.FirstOrDefault(x => x.Alias.InvariantEquals(propertyType.Alias));
}
}
public bool TryGetValue(string propertyTypeAlias, out IProperty property)
{
property = this.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias));
return property != null;
}
/// <summary>
/// Occurs when the collection changes.
/// </summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
CollectionChanged?.Invoke(this, args);
}
/// <inheritdoc />
public void EnsurePropertyTypes(IEnumerable<IPropertyType> propertyTypes)
{
if (propertyTypes == null)
return;
foreach (var propertyType in propertyTypes)
Add(new Property(propertyType));
}
/// <inheritdoc />
public void EnsureCleanPropertyTypes(IEnumerable<IPropertyType> propertyTypes)
{
if (propertyTypes == null)
return;
var propertyTypesA = propertyTypes.ToArray();
var thisAliases = this.Select(x => x.Alias);
var typeAliases = propertyTypesA.Select(x => x.Alias);
var remove = thisAliases.Except(typeAliases).ToArray();
foreach (var alias in remove)
Remove(alias);
foreach (var propertyType in propertyTypesA)
Add(new Property(propertyType));
}
/// <summary>
/// Deep clones.
/// </summary>
public object DeepClone()
{
var clone = new PropertyCollection();
foreach (var property in this)
clone.Add((Property) property.DeepClone());
return clone;
}
}
}