using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using System.Reflection; 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 { public Macro() { _properties = new MacroPropertyCollection(); _properties.CollectionChanged += PropertiesChanged; _addedProperties = new List(); _removedProperties = new List(); } /// /// Creates an item with pre-filled properties /// /// /// /// /// /// /// /// /// /// /// /// /// /// public Macro(int id, Guid key, bool useInEditor, int cacheDuration, string @alias, string name, string controlType, string controlAssembly, string xsltPath, bool cacheByPage, bool cacheByMember, bool dontRender, string scriptPath) : this() { Id = id; Key = key; UseInEditor = useInEditor; CacheDuration = cacheDuration; Alias = alias.ToCleanString(CleanStringType.Alias); Name = name; ControlType = controlType; ControlAssembly = controlAssembly; XsltPath = xsltPath; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; ScriptPath = scriptPath; } /// /// Creates an instance for persisting a new item /// /// /// /// /// /// /// /// /// /// /// /// public Macro(string @alias, string name, string controlType = "", string controlAssembly = "", string xsltPath = "", string scriptPath = "", bool cacheByPage = false, bool cacheByMember = false, bool dontRender = true, bool useInEditor = false, int cacheDuration = 0) : this() { UseInEditor = useInEditor; CacheDuration = cacheDuration; Alias = alias.ToCleanString(CleanStringType.Alias); Name = name; ControlType = controlType; ControlAssembly = controlAssembly; XsltPath = xsltPath; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; ScriptPath = scriptPath; } private string _alias; private string _name; private bool _useInEditor; private int _cacheDuration; private bool _cacheByPage; private bool _cacheByMember; private bool _dontRender; private string _scriptFile; private string _scriptAssembly; private string _scriptPath; private string _xslt; private MacroPropertyCollection _properties; private List _addedProperties; private List _removedProperties; private static readonly Lazy Ps = new Lazy(); private class PropertySelectors { public readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); public readonly PropertyInfo UseInEditorSelector = ExpressionHelper.GetPropertyInfo(x => x.UseInEditor); public readonly PropertyInfo CacheDurationSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheDuration); public readonly PropertyInfo CacheByPageSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByPage); public readonly PropertyInfo CacheByMemberSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByMember); public readonly PropertyInfo DontRenderSelector = ExpressionHelper.GetPropertyInfo(x => x.DontRender); public readonly PropertyInfo ControlPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ControlType); public readonly PropertyInfo ControlAssemblySelector = ExpressionHelper.GetPropertyInfo(x => x.ControlAssembly); public readonly PropertyInfo ScriptPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ScriptPath); public readonly PropertyInfo XsltPathSelector = ExpressionHelper.GetPropertyInfo(x => x.XsltPath); public readonly PropertyInfo PropertiesSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); } void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) { OnPropertyChanged(Ps.Value.PropertiesSelector); 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(Ps.Value.PropertiesSelector); } public override void ResetDirtyProperties(bool rememberDirty) { _addedProperties.Clear(); _removedProperties.Clear(); base.ResetDirtyProperties(rememberDirty); foreach (var prop in Properties) { ((BeingDirtyBase)prop).ResetDirtyProperties(rememberDirty); } } /// /// Used internally to check if we need to add a section in the repository to the db /// internal IEnumerable AddedProperties { get { return _addedProperties; } } /// /// Used internally to check if we need to remove a section in the repository to the db /// internal IEnumerable RemovedProperties { get { return _removedProperties; } } /// /// Gets or sets the alias of the Macro /// [DataMember] public string Alias { get { return _alias; } set { SetPropertyValueAndDetectChanges(value.ToCleanString(CleanStringType.Alias), ref _alias, Ps.Value.AliasSelector); } } /// /// Gets or sets the name of the Macro /// [DataMember] public string Name { get { return _name; } set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); } } /// /// Gets or sets a boolean indicating whether the Macro can be used in an Editor /// [DataMember] public bool UseInEditor { get { return _useInEditor; } set { SetPropertyValueAndDetectChanges(value, ref _useInEditor, Ps.Value.UseInEditorSelector); } } /// /// Gets or sets the Cache Duration for the Macro /// [DataMember] public int CacheDuration { get { return _cacheDuration; } set { SetPropertyValueAndDetectChanges(value, ref _cacheDuration, Ps.Value.CacheDurationSelector); } } /// /// Gets or sets a boolean indicating whether the Macro should be Cached by Page /// [DataMember] public bool CacheByPage { get { return _cacheByPage; } set { SetPropertyValueAndDetectChanges(value, ref _cacheByPage, Ps.Value.CacheByPageSelector); } } /// /// Gets or sets a boolean indicating whether the Macro should be Cached Personally /// [DataMember] public bool CacheByMember { get { return _cacheByMember; } set { SetPropertyValueAndDetectChanges(value, ref _cacheByMember, Ps.Value.CacheByMemberSelector); } } /// /// Gets or sets a boolean indicating whether the Macro should be rendered in an Editor /// [DataMember] public bool DontRender { get { return _dontRender; } set { SetPropertyValueAndDetectChanges(value, ref _dontRender, Ps.Value.DontRenderSelector); } } /// /// Gets or sets the path to user control or the Control Type to render /// [DataMember] public string ControlType { get { return _scriptFile; } set { SetPropertyValueAndDetectChanges(value, ref _scriptFile, Ps.Value.ControlPathSelector); } } /// /// Gets or sets the name of the assembly, which should be used by the Macro /// /// Will usually only be filled if the ControlType is a Usercontrol [DataMember] public string ControlAssembly { get { return _scriptAssembly; } set { SetPropertyValueAndDetectChanges(value, ref _scriptAssembly, Ps.Value.ControlAssemblySelector); } } /// /// Gets or set the path to the Python file in use /// /// Optional: Can only be one of three Script, Python or Xslt [DataMember] public string ScriptPath { get { return _scriptPath; } set { SetPropertyValueAndDetectChanges(value, ref _scriptPath, Ps.Value.ScriptPathSelector); } } /// /// Gets or sets the path to the Xslt file in use /// /// Optional: Can only be one of three Script, Python or Xslt [DataMember] public string XsltPath { get { return _xslt; } set { SetPropertyValueAndDetectChanges(value, ref _xslt, Ps.Value.XsltPathSelector); } } /// /// Gets or sets a list of Macro Properties /// [DataMember] public MacroPropertyCollection Properties { get { return _properties; } } public override object DeepClone() { var clone = (Macro)base.DeepClone(); //turn off change tracking clone.DisableChangeTracking(); clone._addedProperties = new List(); clone._removedProperties = new List(); clone._properties = (MacroPropertyCollection)Properties.DeepClone(); //re-assign the event handler clone._properties.CollectionChanged += clone.PropertiesChanged; //this shouldn't really be needed since we're not tracking clone.ResetDirtyProperties(false); //re-enable tracking clone.EnableChangeTracking(); return clone; } } }