using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using System.Runtime.Serialization; using Umbraco.Core.Models.Entities; using Umbraco.Core.Strings; namespace Umbraco.Core.Models { /// /// Represents a Macro /// [Serializable] [DataContract(IsReference = true)] public class Macro : EntityBase, IMacro { private readonly IShortStringHelper _shortStringHelper; public Macro(IShortStringHelper shortStringHelper) { _shortStringHelper = shortStringHelper; _properties = new MacroPropertyCollection(); _properties.CollectionChanged += PropertiesChanged; _addedProperties = new List(); _removedProperties = new List(); } /// /// Creates an item with pre-filled properties /// /// /// /// /// /// /// /// /// /// /// public Macro(IShortStringHelper shortStringHelper, int id, Guid key, bool useInEditor, int cacheDuration, string @alias, string name, bool cacheByPage, bool cacheByMember, bool dontRender, string macroSource) : this(shortStringHelper) { Id = id; Key = key; UseInEditor = useInEditor; CacheDuration = cacheDuration; Alias = alias.ToCleanString(shortStringHelper,CleanStringType.Alias); Name = name; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; MacroSource = macroSource; } /// /// Creates an instance for persisting a new item /// /// /// /// /// /// /// /// /// public Macro(IShortStringHelper shortStringHelper, string @alias, string name, string macroSource, bool cacheByPage = false, bool cacheByMember = false, bool dontRender = true, bool useInEditor = false, int cacheDuration = 0) : this(shortStringHelper) { UseInEditor = useInEditor; CacheDuration = cacheDuration; Alias = alias.ToCleanString(shortStringHelper, CleanStringType.Alias); Name = name; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; MacroSource = macroSource; } private string _alias; private string _name; private bool _useInEditor; private int _cacheDuration; private bool _cacheByPage; private bool _cacheByMember; private bool _dontRender; private string _macroSource; private MacroPropertyCollection _properties; private List _addedProperties; private List _removedProperties; void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) { OnPropertyChanged(nameof(Properties)); if (e.Action == NotifyCollectionChangedAction.Add) { //listen for changes var prop = e.NewItems.Cast().First(); prop.PropertyChanged += PropertyDataChanged; var alias = prop.Alias; if (_addedProperties.Contains(alias) == false) { //add to the added props _addedProperties.Add(alias); } } else if (e.Action == NotifyCollectionChangedAction.Remove) { //remove listening for changes var prop = e.OldItems.Cast().First(); prop.PropertyChanged -= PropertyDataChanged; var alias = prop.Alias; if (_removedProperties.Contains(alias) == false) { _removedProperties.Add(alias); } } } /// /// When some data of a property has changed ensure our Properties flag is dirty /// /// /// void PropertyDataChanged(object sender, PropertyChangedEventArgs e) { OnPropertyChanged(nameof(Properties)); } public override void ResetDirtyProperties(bool rememberDirty) { base.ResetDirtyProperties(rememberDirty); _addedProperties.Clear(); _removedProperties.Clear(); foreach (var prop in Properties) { prop.ResetDirtyProperties(rememberDirty); } } /// /// Used internally to check if we need to add a section in the repository to the db /// internal IEnumerable AddedProperties => _addedProperties; /// /// Used internally to check if we need to remove a section in the repository to the db /// internal IEnumerable RemovedProperties => _removedProperties; /// /// Gets or sets the alias of the Macro /// [DataMember] public string Alias { get => _alias; set => SetPropertyValueAndDetectChanges(value.ToCleanString(_shortStringHelper, CleanStringType.Alias), ref _alias, nameof(Alias)); } /// /// Gets or sets the name of the Macro /// [DataMember] public string Name { get => _name; set => SetPropertyValueAndDetectChanges(value, ref _name, nameof(Name)); } /// /// Gets or sets a boolean indicating whether the Macro can be used in an Editor /// [DataMember] public bool UseInEditor { get => _useInEditor; set => SetPropertyValueAndDetectChanges(value, ref _useInEditor, nameof(UseInEditor)); } /// /// Gets or sets the Cache Duration for the Macro /// [DataMember] public int CacheDuration { get => _cacheDuration; set => SetPropertyValueAndDetectChanges(value, ref _cacheDuration, nameof(CacheDuration)); } /// /// Gets or sets a boolean indicating whether the Macro should be Cached by Page /// [DataMember] public bool CacheByPage { get => _cacheByPage; set => SetPropertyValueAndDetectChanges(value, ref _cacheByPage, nameof(CacheByPage)); } /// /// Gets or sets a boolean indicating whether the Macro should be Cached Personally /// [DataMember] public bool CacheByMember { get => _cacheByMember; set => SetPropertyValueAndDetectChanges(value, ref _cacheByMember, nameof(CacheByMember)); } /// /// Gets or sets a boolean indicating whether the Macro should be rendered in an Editor /// [DataMember] public bool DontRender { get => _dontRender; set => SetPropertyValueAndDetectChanges(value, ref _dontRender, nameof(DontRender)); } /// /// Gets or set the path to the Partial View to render /// [DataMember] public string MacroSource { get => _macroSource; set => SetPropertyValueAndDetectChanges(value, ref _macroSource, nameof(MacroSource)); } /// /// Gets or sets a list of Macro Properties /// [DataMember] public MacroPropertyCollection Properties => _properties; protected override void PerformDeepClone(object clone) { base.PerformDeepClone(clone); var clonedEntity = (Macro)clone; clonedEntity._addedProperties = new List(); clonedEntity._removedProperties = new List(); clonedEntity._properties = (MacroPropertyCollection)Properties.DeepClone(); //re-assign the event handler clonedEntity._properties.CollectionChanged += clonedEntity.PropertiesChanged; } } }