Revert "Temp8 tinymce"
This commit is contained in:
@@ -1,170 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
[DebuggerDisplay("Tree - {Title} ({ApplicationAlias})")]
|
||||
public class ApplicationTree
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, Type> ResolvedTypes = new ConcurrentDictionary<string, Type>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationTree"/> class.
|
||||
/// </summary>
|
||||
public ApplicationTree() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationTree"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialize">if set to <c>true</c> [initialize].</param>
|
||||
/// <param name="sortOrder">The sort order.</param>
|
||||
/// <param name="applicationAlias">The application alias.</param>
|
||||
/// <param name="alias">The tree alias.</param>
|
||||
/// <param name="title">The tree title.</param>
|
||||
/// <param name="iconClosed">The icon closed.</param>
|
||||
/// <param name="iconOpened">The icon opened.</param>
|
||||
/// <param name="type">The tree type.</param>
|
||||
public ApplicationTree(bool initialize, int sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type)
|
||||
{
|
||||
Initialize = initialize;
|
||||
SortOrder = sortOrder;
|
||||
ApplicationAlias = applicationAlias;
|
||||
Alias = alias;
|
||||
Title = title;
|
||||
IconClosed = iconClosed;
|
||||
IconOpened = iconOpened;
|
||||
Type = type;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="ApplicationTree"/> should initialize.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if initialize; otherwise, <c>false</c>.</value>
|
||||
public bool Initialize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order.
|
||||
/// </summary>
|
||||
/// <value>The sort order.</value>
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application alias.
|
||||
/// </summary>
|
||||
/// <value>The application alias.</value>
|
||||
public string ApplicationAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tree alias.
|
||||
/// </summary>
|
||||
/// <value>The alias.</value>
|
||||
public string Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tree title.
|
||||
/// </summary>
|
||||
/// <value>The title.</value>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon closed.
|
||||
/// </summary>
|
||||
/// <value>The icon closed.</value>
|
||||
public string IconClosed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon opened.
|
||||
/// </summary>
|
||||
/// <value>The icon opened.</value>
|
||||
public string IconOpened { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tree type assembly name.
|
||||
/// </summary>
|
||||
/// <value>The type.</value>
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the localized root node display name
|
||||
/// </summary>
|
||||
/// <param name="textService"></param>
|
||||
/// <returns></returns>
|
||||
public string GetRootNodeDisplayName(ILocalizedTextService textService)
|
||||
{
|
||||
var label = $"[{Alias}]";
|
||||
|
||||
// try to look up a the localized tree header matching the tree alias
|
||||
var localizedLabel = textService.Localize("treeHeaders/" + Alias);
|
||||
|
||||
// if the localizedLabel returns [alias] then return the title attribute from the trees.config file, if it's defined
|
||||
if (localizedLabel != null && localizedLabel.Equals(label, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Title) == false)
|
||||
label = Title;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the localizedLabel translated into something that's not just [alias], so use the translation
|
||||
label = localizedLabel;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
private Type _runtimeType;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CLR type based on it's assembly name stored in the config
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type GetRuntimeType()
|
||||
{
|
||||
return _runtimeType ?? (_runtimeType = System.Type.GetType(Type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to try to get and cache the tree type
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
internal static Type TryGetType(string type)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ResolvedTypes.GetOrAdd(type, s =>
|
||||
{
|
||||
var result = System.Type.GetType(type);
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//we need to implement a bit of a hack here due to some trees being renamed and backwards compat
|
||||
var parts = type.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length != 2)
|
||||
throw new InvalidOperationException("Could not resolve type");
|
||||
if (parts[1].Trim() != "Umbraco.Web" || parts[0].StartsWith("Umbraco.Web.Trees") == false || parts[0].EndsWith("Controller"))
|
||||
throw new InvalidOperationException("Could not resolve type");
|
||||
|
||||
//if it's one of our controllers but it's not suffixed with "Controller" then add it and try again
|
||||
var tempType = parts[0] + "Controller, Umbraco.Web";
|
||||
|
||||
result = System.Type.GetType(tempType);
|
||||
if (result != null)
|
||||
return result;
|
||||
|
||||
throw new InvalidOperationException("Could not resolve type");
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
//swallow, this is our own exception, couldn't find the type
|
||||
// fixme bad use of exceptions here!
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
[DebuggerDisplay("Tree - {Title} ({ApplicationAlias})")]
|
||||
public class ApplicationTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationTree"/> class.
|
||||
/// </summary>
|
||||
public ApplicationTree() { }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationTree"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialize">if set to <c>true</c> [initialize].</param>
|
||||
/// <param name="sortOrder">The sort order.</param>
|
||||
/// <param name="applicationAlias">The application alias.</param>
|
||||
/// <param name="alias">The tree alias.</param>
|
||||
/// <param name="title">The tree title.</param>
|
||||
/// <param name="iconClosed">The icon closed.</param>
|
||||
/// <param name="iconOpened">The icon opened.</param>
|
||||
/// <param name="type">The tree type.</param>
|
||||
public ApplicationTree(bool initialize, int sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type)
|
||||
{
|
||||
this.Initialize = initialize;
|
||||
this.SortOrder = sortOrder;
|
||||
this.ApplicationAlias = applicationAlias;
|
||||
this.Alias = alias;
|
||||
this.Title = title;
|
||||
this.IconClosed = iconClosed;
|
||||
this.IconOpened = iconOpened;
|
||||
this.Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="ApplicationTree"/> should initialize.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if initialize; otherwise, <c>false</c>.</value>
|
||||
public bool Initialize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order.
|
||||
/// </summary>
|
||||
/// <value>The sort order.</value>
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application alias.
|
||||
/// </summary>
|
||||
/// <value>The application alias.</value>
|
||||
public string ApplicationAlias { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tree alias.
|
||||
/// </summary>
|
||||
/// <value>The alias.</value>
|
||||
public string Alias { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tree title.
|
||||
/// </summary>
|
||||
/// <value>The title.</value>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon closed.
|
||||
/// </summary>
|
||||
/// <value>The icon closed.</value>
|
||||
public string IconClosed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon opened.
|
||||
/// </summary>
|
||||
/// <value>The icon opened.</value>
|
||||
public string IconOpened { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tree type assembly name.
|
||||
/// </summary>
|
||||
/// <value>The type.</value>
|
||||
public string Type { get; set; }
|
||||
|
||||
private Type _runtimeType;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CLR type based on it's assembly name stored in the config
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type GetRuntimeType()
|
||||
{
|
||||
if (_runtimeType != null)
|
||||
return _runtimeType;
|
||||
|
||||
_runtimeType = TryGetType(Type);
|
||||
return _runtimeType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to try to get and cache the tree type
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
internal static Type TryGetType(string type)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ResolvedTypes.GetOrAdd(type, s =>
|
||||
{
|
||||
var result = System.Type.GetType(type);
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//we need to implement a bit of a hack here due to some trees being renamed and backwards compat
|
||||
var parts = type.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
if (parts[1].Trim() == "umbraco" && parts[0].StartsWith("Umbraco.Web.Trees") && parts[0].EndsWith("Controller") == false)
|
||||
{
|
||||
//if it's one of our controllers but it's not suffixed with "Controller" then add it and try again
|
||||
var tempType = parts[0] + "Controller, umbraco";
|
||||
|
||||
result = System.Type.GetType(tempType);
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Could not resolve type");
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
//swallow, this is our own exception, couldn't find the type
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<string, Type> ResolvedTypes = new ConcurrentDictionary<string, Type>();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class AuditEntry : EntityBase, IAuditEntry
|
||||
internal class AuditEntry : Entity, IAuditEntry
|
||||
{
|
||||
private static PropertySelectors _selectors;
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public sealed class AuditItem : EntityBase, IAuditItem
|
||||
public sealed class AuditItem : Entity, IAuditItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuditItem"/> class.
|
||||
/// Constructor for creating an item to be created
|
||||
/// </summary>
|
||||
public AuditItem(int objectId, AuditType type, int userId, string entityType, string comment = null, string parameters = null)
|
||||
/// <param name="objectId"></param>
|
||||
/// <param name="comment"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="userId"></param>
|
||||
public AuditItem(int objectId, string comment, AuditType type, int userId)
|
||||
{
|
||||
DisableChangeTracking();
|
||||
|
||||
@@ -15,25 +19,13 @@ namespace Umbraco.Core.Models
|
||||
Comment = comment;
|
||||
AuditType = type;
|
||||
UserId = userId;
|
||||
EntityType = entityType;
|
||||
Parameters = parameters;
|
||||
|
||||
EnableChangeTracking();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AuditType AuditType { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string EntityType { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int UserId { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Comment { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Parameters { get; }
|
||||
public string Comment { get; private set; }
|
||||
public AuditType AuditType { get; private set; }
|
||||
public int UserId { get; private set; }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,118 +1,85 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines audit types.
|
||||
/// Enums for vailable types of auditing
|
||||
/// </summary>
|
||||
public enum AuditType
|
||||
{
|
||||
/// <summary>
|
||||
/// New node(s) being added.
|
||||
/// Used when new nodes are added
|
||||
/// </summary>
|
||||
New,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being saved.
|
||||
/// Used when nodes are saved
|
||||
/// </summary>
|
||||
Save,
|
||||
|
||||
/// <summary>
|
||||
/// Variant(s) being saved.
|
||||
/// </summary>
|
||||
SaveVariant,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being opened.
|
||||
/// Used when nodes are opened
|
||||
/// </summary>
|
||||
Open,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being deleted.
|
||||
/// Used when nodes are deleted
|
||||
/// </summary>
|
||||
Delete,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being published.
|
||||
/// Used when nodes are published
|
||||
/// </summary>
|
||||
Publish,
|
||||
|
||||
/// <summary>
|
||||
/// Variant(s) being published.
|
||||
/// </summary>
|
||||
PublishVariant,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being sent to publishing.
|
||||
/// Used when nodes are send to publishing
|
||||
/// </summary>
|
||||
SendToPublish,
|
||||
|
||||
/// <summary>
|
||||
/// Variant(s) being sent to publishing.
|
||||
/// </summary>
|
||||
SendToPublishVariant,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being unpublished.
|
||||
/// Used when nodes are unpublished
|
||||
/// </summary>
|
||||
Unpublish,
|
||||
|
||||
UnPublish,
|
||||
/// <summary>
|
||||
/// Variant(s) being unpublished.
|
||||
/// </summary>
|
||||
UnpublishVariant,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being moved.
|
||||
/// Used when nodes are moved
|
||||
/// </summary>
|
||||
Move,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being copied.
|
||||
/// Used when nodes are copied
|
||||
/// </summary>
|
||||
Copy,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being assigned domains.
|
||||
/// Used when nodes are assígned a domain
|
||||
/// </summary>
|
||||
AssignDomain,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) public access changing.
|
||||
/// Used when public access are changed for a node
|
||||
/// </summary>
|
||||
PublicAccess,
|
||||
|
||||
/// <summary>
|
||||
/// Node(s) being sorted.
|
||||
/// Used when nodes are sorted
|
||||
/// </summary>
|
||||
Sort,
|
||||
|
||||
/// <summary>
|
||||
/// Notification(s) being sent to user.
|
||||
/// Used when a notification are send to a user
|
||||
/// </summary>
|
||||
Notify,
|
||||
|
||||
/// <summary>
|
||||
/// General system audit message.
|
||||
/// General system notification
|
||||
/// </summary>
|
||||
System,
|
||||
|
||||
/// <summary>
|
||||
/// Node's content being rolled back to a previous version.
|
||||
/// Used when a node's content is rolled back to a previous version
|
||||
/// </summary>
|
||||
RollBack,
|
||||
|
||||
/// <summary>
|
||||
/// Package being installed.
|
||||
/// Used when a package is installed
|
||||
/// </summary>
|
||||
PackagerInstall,
|
||||
|
||||
/// <summary>
|
||||
/// Package being uninstalled.
|
||||
/// Used when a package is uninstalled
|
||||
/// </summary>
|
||||
PackagerUninstall,
|
||||
|
||||
/// <summary>
|
||||
/// Custom audit message.
|
||||
/// Used when a node is send to translation
|
||||
/// </summary>
|
||||
SendToTranslate,
|
||||
/// <summary>
|
||||
/// Use this log action for custom log messages that should be shown in the audit trail
|
||||
/// </summary>
|
||||
Custom
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class Consent : EntityBase, IConsent
|
||||
internal class Consent : Entity, IConsent
|
||||
{
|
||||
private static PropertySelectors _selector;
|
||||
|
||||
|
||||
+343
-524
@@ -1,524 +1,343 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Content object
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class Content : ContentBase, IContent
|
||||
{
|
||||
private IContentType _contentType;
|
||||
private ITemplate _template;
|
||||
private ContentScheduleCollection _schedule;
|
||||
private bool _published;
|
||||
private PublishedState _publishedState;
|
||||
private ContentCultureInfosCollection _publishInfos;
|
||||
private ContentCultureInfosCollection _publishInfosOrig;
|
||||
private HashSet<string> _editedCultures;
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Content object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="parent">Parent <see cref="IContent"/> object</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
/// <param name="culture">An optional culture.</param>
|
||||
public Content(string name, IContent parent, IContentType contentType, string culture = null)
|
||||
: this(name, parent, contentType, new PropertyCollection(), culture)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Content object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="parent">Parent <see cref="IContent"/> object</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
/// <param name="properties">Collection of properties</param>
|
||||
/// <param name="culture">An optional culture.</param>
|
||||
public Content(string name, IContent parent, IContentType contentType, PropertyCollection properties, string culture = null)
|
||||
: base(name, parent, contentType, properties, culture)
|
||||
{
|
||||
_contentType = contentType ?? throw new ArgumentNullException(nameof(contentType));
|
||||
_publishedState = PublishedState.Unpublished;
|
||||
PublishedVersionId = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Content object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="parentId">Id of the Parent content</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
/// <param name="culture">An optional culture.</param>
|
||||
public Content(string name, int parentId, IContentType contentType, string culture = null)
|
||||
: this(name, parentId, contentType, new PropertyCollection(), culture)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Content object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="parentId">Id of the Parent content</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
/// <param name="properties">Collection of properties</param>
|
||||
/// <param name="culture">An optional culture.</param>
|
||||
public Content(string name, int parentId, IContentType contentType, PropertyCollection properties, string culture = null)
|
||||
: base(name, parentId, contentType, properties, culture)
|
||||
{
|
||||
_contentType = contentType ?? throw new ArgumentNullException(nameof(contentType));
|
||||
_publishedState = PublishedState.Unpublished;
|
||||
PublishedVersionId = 0;
|
||||
}
|
||||
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo TemplateSelector = ExpressionHelper.GetPropertyInfo<Content, ITemplate>(x => x.Template);
|
||||
public readonly PropertyInfo PublishedSelector = ExpressionHelper.GetPropertyInfo<Content, bool>(x => x.Published);
|
||||
public readonly PropertyInfo ContentScheduleSelector = ExpressionHelper.GetPropertyInfo<Content, ContentScheduleCollection>(x => x.ContentSchedule);
|
||||
public readonly PropertyInfo PublishCultureInfosSelector = ExpressionHelper.GetPropertyInfo<Content, IReadOnlyDictionary<string, ContentCultureInfos>>(x => x.PublishCultureInfos);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DoNotClone]
|
||||
public ContentScheduleCollection ContentSchedule
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_schedule == null)
|
||||
{
|
||||
_schedule = new ContentScheduleCollection();
|
||||
_schedule.CollectionChanged += ScheduleCollectionChanged;
|
||||
}
|
||||
return _schedule;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(_schedule != null)
|
||||
_schedule.CollectionChanged -= ScheduleCollectionChanged;
|
||||
SetPropertyValueAndDetectChanges(value, ref _schedule, Ps.Value.ContentScheduleSelector);
|
||||
if (_schedule != null)
|
||||
_schedule.CollectionChanged += ScheduleCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collection changed event handler to ensure the schedule field is set to dirty when the schedule changes
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ScheduleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(Ps.Value.ContentScheduleSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template used by the Content.
|
||||
/// This is used to override the default one from the ContentType.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If no template is explicitly set on the Content object,
|
||||
/// the Default template from the ContentType will be returned.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public ITemplate Template
|
||||
{
|
||||
get => _template ?? _contentType.DefaultTemplate;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _template, Ps.Value.TemplateSelector);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this content item is published or not.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public bool Published
|
||||
{
|
||||
get => _published;
|
||||
|
||||
// the setter is internal and should only be invoked from
|
||||
// - the ContentFactory when creating a content entity from a dto
|
||||
// - the ContentRepository when updating a content entity
|
||||
internal set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _published, Ps.Value.PublishedSelector);
|
||||
_publishedState = _published ? PublishedState.Published : PublishedState.Unpublished;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the published state of the content item.
|
||||
/// </summary>
|
||||
/// <remarks>The state should be Published or Unpublished, depending on whether Published
|
||||
/// is true or false, but can also temporarily be Publishing or Unpublishing when the
|
||||
/// content item is about to be saved.</remarks>
|
||||
[DataMember]
|
||||
public PublishedState PublishedState
|
||||
{
|
||||
get => _publishedState;
|
||||
set
|
||||
{
|
||||
if (value != PublishedState.Publishing && value != PublishedState.Unpublishing)
|
||||
throw new ArgumentException("Invalid state, only Publishing and Unpublishing are accepted.");
|
||||
_publishedState = value;
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public bool Edited { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this content object
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public IContentType ContentType => _contentType;
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
public DateTime? PublishDate { get; internal set; } // set by persistence
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
public int? PublisherId { get; internal set; } // set by persistence
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
public ITemplate PublishTemplate { get; internal set; } // set by persistence
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
public string PublishName { get; internal set; } // set by persistence
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
public IEnumerable<string> EditedCultures => CultureInfos.Keys.Where(IsCultureEdited);
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
public IEnumerable<string> PublishedCultures => _publishInfos?.Keys ?? Enumerable.Empty<string>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsCulturePublished(string culture)
|
||||
// just check _publishInfos
|
||||
// a non-available culture could not become published anyways
|
||||
=> _publishInfos != null && _publishInfos.ContainsKey(culture);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool WasCulturePublished(string culture)
|
||||
// just check _publishInfosOrig - a copy of _publishInfos
|
||||
// a non-available culture could not become published anyways
|
||||
=> _publishInfosOrig != null && _publishInfosOrig.ContainsKey(culture);
|
||||
|
||||
// adjust dates to sync between version, cultures etc
|
||||
// used by the repo when persisting
|
||||
internal void AdjustDates(DateTime date)
|
||||
{
|
||||
foreach (var culture in PublishedCultures.ToList())
|
||||
{
|
||||
if (_publishInfos == null || !_publishInfos.TryGetValue(culture, out var publishInfos))
|
||||
continue;
|
||||
|
||||
if (_publishInfosOrig != null && _publishInfosOrig.TryGetValue(culture, out var publishInfosOrig)
|
||||
&& publishInfosOrig.Date == publishInfos.Date)
|
||||
continue;
|
||||
|
||||
_publishInfos.AddOrUpdate(culture, publishInfos.Name, date);
|
||||
|
||||
if (CultureInfos.TryGetValue(culture, out var infos))
|
||||
SetCultureInfo(culture, infos.Name, date);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsCultureEdited(string culture)
|
||||
=> IsCultureAvailable(culture) && // is available, and
|
||||
(!IsCulturePublished(culture) || // is not published, or
|
||||
(_editedCultures != null && _editedCultures.Contains(culture))); // is edited
|
||||
|
||||
/// <inheritdoc/>
|
||||
[IgnoreDataMember]
|
||||
public IReadOnlyDictionary<string, ContentCultureInfos> PublishCultureInfos => _publishInfos ?? NoInfos;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string GetPublishName(string culture)
|
||||
{
|
||||
if (culture.IsNullOrWhiteSpace()) return PublishName;
|
||||
if (!ContentTypeBase.VariesByCulture()) return null;
|
||||
if (_publishInfos == null) return null;
|
||||
return _publishInfos.TryGetValue(culture, out var infos) ? infos.Name : null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? GetPublishDate(string culture)
|
||||
{
|
||||
if (culture.IsNullOrWhiteSpace()) return PublishDate;
|
||||
if (!ContentTypeBase.VariesByCulture()) return null;
|
||||
if (_publishInfos == null) return null;
|
||||
return _publishInfos.TryGetValue(culture, out var infos) ? infos.Date : (DateTime?) null;
|
||||
}
|
||||
|
||||
// internal for repository
|
||||
internal void SetPublishInfo(string culture, string name, DateTime date)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
throw new ArgumentNullOrEmptyException(nameof(name));
|
||||
|
||||
if (culture.IsNullOrWhiteSpace())
|
||||
throw new ArgumentNullOrEmptyException(nameof(culture));
|
||||
|
||||
if (_publishInfos == null)
|
||||
{
|
||||
_publishInfos = new ContentCultureInfosCollection();
|
||||
_publishInfos.CollectionChanged += PublishNamesCollectionChanged;
|
||||
}
|
||||
|
||||
_publishInfos.AddOrUpdate(culture, name, date);
|
||||
}
|
||||
|
||||
private void ClearPublishInfos()
|
||||
{
|
||||
_publishInfos = null;
|
||||
}
|
||||
|
||||
private void ClearPublishInfo(string culture)
|
||||
{
|
||||
if (culture.IsNullOrWhiteSpace())
|
||||
throw new ArgumentNullOrEmptyException(nameof(culture));
|
||||
|
||||
if (_publishInfos == null) return;
|
||||
_publishInfos.Remove(culture);
|
||||
if (_publishInfos.Count == 0) _publishInfos = null;
|
||||
|
||||
// set the culture to be dirty - it's been modified
|
||||
TouchCultureInfo(culture);
|
||||
}
|
||||
|
||||
// sets a publish edited
|
||||
internal void SetCultureEdited(string culture)
|
||||
{
|
||||
if (culture.IsNullOrWhiteSpace())
|
||||
throw new ArgumentNullOrEmptyException(nameof(culture));
|
||||
if (_editedCultures == null)
|
||||
_editedCultures = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
_editedCultures.Add(culture.ToLowerInvariant());
|
||||
}
|
||||
|
||||
// sets all publish edited
|
||||
internal void SetCultureEdited(IEnumerable<string> cultures)
|
||||
{
|
||||
if (cultures == null)
|
||||
{
|
||||
_editedCultures = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var editedCultures = new HashSet<string>(cultures.Where(x => !x.IsNullOrWhiteSpace()), StringComparer.OrdinalIgnoreCase);
|
||||
_editedCultures = editedCultures.Count > 0 ? editedCultures : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles culture infos collection changes.
|
||||
/// </summary>
|
||||
private void PublishNamesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(Ps.Value.PublishCultureInfosSelector);
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public int PublishedVersionId { get; internal set; }
|
||||
|
||||
[DataMember]
|
||||
public bool Blueprint { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool PublishCulture(string culture = "*")
|
||||
{
|
||||
culture = culture.NullOrWhiteSpaceAsNull();
|
||||
|
||||
// the variation should be supported by the content type properties
|
||||
// if the content type is invariant, only '*' and 'null' is ok
|
||||
// if the content type varies, everything is ok because some properties may be invariant
|
||||
if (!ContentType.SupportsPropertyVariation(culture, "*", true))
|
||||
throw new NotSupportedException($"Culture \"{culture}\" is not supported by content type \"{ContentType.Alias}\" with variation \"{ContentType.Variations}\".");
|
||||
|
||||
// the values we want to publish should be valid
|
||||
if (ValidateProperties(culture).Any())
|
||||
return false;
|
||||
|
||||
var alsoInvariant = false;
|
||||
if (culture == "*") // all cultures
|
||||
{
|
||||
foreach (var c in AvailableCultures)
|
||||
{
|
||||
var name = GetCultureName(c);
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return false;
|
||||
SetPublishInfo(c, name, DateTime.Now);
|
||||
}
|
||||
}
|
||||
else if (culture == null) // invariant culture
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Name))
|
||||
return false;
|
||||
// PublishName set by repository - nothing to do here
|
||||
}
|
||||
else // one single culture
|
||||
{
|
||||
var name = GetCultureName(culture);
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return false;
|
||||
SetPublishInfo(culture, name, DateTime.Now);
|
||||
alsoInvariant = true; // we also want to publish invariant values
|
||||
}
|
||||
|
||||
// property.PublishValues only publishes what is valid, variation-wise
|
||||
foreach (var property in Properties)
|
||||
{
|
||||
property.PublishValues(culture);
|
||||
if (alsoInvariant)
|
||||
property.PublishValues(null);
|
||||
}
|
||||
|
||||
_publishedState = PublishedState.Publishing;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UnpublishCulture(string culture = "*")
|
||||
{
|
||||
culture = culture.NullOrWhiteSpaceAsNull();
|
||||
|
||||
// the variation should be supported by the content type properties
|
||||
if (!ContentType.SupportsPropertyVariation(culture, "*", true))
|
||||
throw new NotSupportedException($"Culture \"{culture}\" is not supported by content type \"{ContentType.Alias}\" with variation \"{ContentType.Variations}\".");
|
||||
|
||||
if (culture == "*") // all cultures
|
||||
ClearPublishInfos();
|
||||
else // one single culture
|
||||
ClearPublishInfo(culture);
|
||||
|
||||
// property.PublishValues only publishes what is valid, variation-wise
|
||||
foreach (var property in Properties)
|
||||
property.UnpublishValues(culture);
|
||||
|
||||
_publishedState = PublishedState.Publishing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="ContentType"/> for the current content object
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <remarks>Leaves PropertyTypes intact after change</remarks>
|
||||
public void ChangeContentType(IContentType contentType)
|
||||
{
|
||||
ContentTypeId = contentType.Id;
|
||||
_contentType = contentType;
|
||||
ContentTypeBase = contentType;
|
||||
Properties.EnsurePropertyTypes(PropertyTypes);
|
||||
|
||||
Properties.CollectionChanged -= PropertiesChanged; // be sure not to double add
|
||||
Properties.CollectionChanged += PropertiesChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="ContentType"/> for the current content object and removes PropertyTypes,
|
||||
/// which are not part of the new ContentType.
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <param name="clearProperties">Boolean indicating whether to clear PropertyTypes upon change</param>
|
||||
public void ChangeContentType(IContentType contentType, bool clearProperties)
|
||||
{
|
||||
if(clearProperties)
|
||||
{
|
||||
ContentTypeId = contentType.Id;
|
||||
_contentType = contentType;
|
||||
ContentTypeBase = contentType;
|
||||
Properties.EnsureCleanPropertyTypes(PropertyTypes);
|
||||
|
||||
Properties.CollectionChanged -= PropertiesChanged; // be sure not to double add
|
||||
Properties.CollectionChanged += PropertiesChanged;
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeContentType(contentType);
|
||||
}
|
||||
|
||||
public override void ResetDirtyProperties(bool rememberDirty)
|
||||
{
|
||||
base.ResetDirtyProperties(rememberDirty);
|
||||
|
||||
if (Template != null)
|
||||
Template.ResetDirtyProperties(rememberDirty);
|
||||
if (ContentType != null)
|
||||
ContentType.ResetDirtyProperties(rememberDirty);
|
||||
|
||||
// take care of the published state
|
||||
_publishedState = _published ? PublishedState.Published : PublishedState.Unpublished;
|
||||
|
||||
// Make a copy of the _publishInfos, this is purely so that we can detect
|
||||
// if this entity's previous culture publish state (regardless of the rememberDirty flag)
|
||||
_publishInfosOrig = _publishInfos == null
|
||||
? null
|
||||
: new ContentCultureInfosCollection(_publishInfos);
|
||||
|
||||
if (_publishInfos == null) return;
|
||||
|
||||
foreach (var infos in _publishInfos)
|
||||
infos.ResetDirtyProperties(rememberDirty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContent DeepCloneWithResetIdentities()
|
||||
{
|
||||
var clone = (Content)DeepClone();
|
||||
clone.Key = Guid.Empty;
|
||||
clone.VersionId = clone.PublishedVersionId = 0;
|
||||
clone.ResetIdentity();
|
||||
|
||||
foreach (var property in clone.Properties)
|
||||
property.ResetIdentity();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected override void PerformDeepClone(object clone)
|
||||
{
|
||||
base.PerformDeepClone(clone);
|
||||
|
||||
var clonedContent = (Content)clone;
|
||||
|
||||
//need to manually clone this since it's not settable
|
||||
clonedContent._contentType = (IContentType) ContentType.DeepClone();
|
||||
|
||||
//if culture infos exist then deal with event bindings
|
||||
if (clonedContent._publishInfos != null)
|
||||
{
|
||||
clonedContent._publishInfos.CollectionChanged -= PublishNamesCollectionChanged; //clear this event handler if any
|
||||
clonedContent._publishInfos = (ContentCultureInfosCollection) _publishInfos.DeepClone(); //manually deep clone
|
||||
clonedContent._publishInfos.CollectionChanged += clonedContent.PublishNamesCollectionChanged; //re-assign correct event handler
|
||||
}
|
||||
|
||||
//if properties exist then deal with event bindings
|
||||
if (clonedContent._schedule != null)
|
||||
{
|
||||
clonedContent._schedule.CollectionChanged -= ScheduleCollectionChanged; //clear this event handler if any
|
||||
clonedContent._schedule = (ContentScheduleCollection)_schedule.DeepClone(); //manually deep clone
|
||||
clonedContent._schedule.CollectionChanged += clonedContent.ScheduleCollectionChanged; //re-assign correct event handler
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Content object
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class Content : ContentBase, IContent
|
||||
{
|
||||
private IContentType _contentType;
|
||||
private ITemplate _template;
|
||||
private bool _published;
|
||||
private string _language;
|
||||
private DateTime? _releaseDate;
|
||||
private DateTime? _expireDate;
|
||||
private int _writer;
|
||||
private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name.
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Content object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="parent">Parent <see cref="IContent"/> object</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
public Content(string name, IContent parent, IContentType contentType)
|
||||
: this(name, parent, contentType, new PropertyCollection())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Content object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="parent">Parent <see cref="IContent"/> object</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
/// <param name="properties">Collection of properties</param>
|
||||
public Content(string name, IContent parent, IContentType contentType, PropertyCollection properties)
|
||||
: base(name, parent, contentType, properties)
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentType = contentType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Content object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="parentId">Id of the Parent content</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
public Content(string name, int parentId, IContentType contentType)
|
||||
: this(name, parentId, contentType, new PropertyCollection())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Content object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="parentId">Id of the Parent content</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
/// <param name="properties">Collection of properties</param>
|
||||
public Content(string name, int parentId, IContentType contentType, PropertyCollection properties)
|
||||
: base(name, parentId, contentType, properties)
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentType = contentType;
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo TemplateSelector = ExpressionHelper.GetPropertyInfo<Content, ITemplate>(x => x.Template);
|
||||
public readonly PropertyInfo PublishedSelector = ExpressionHelper.GetPropertyInfo<Content, bool>(x => x.Published);
|
||||
public readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo<Content, string>(x => x.Language);
|
||||
public readonly PropertyInfo ReleaseDateSelector = ExpressionHelper.GetPropertyInfo<Content, DateTime?>(x => x.ReleaseDate);
|
||||
public readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo<Content, DateTime?>(x => x.ExpireDate);
|
||||
public readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo<Content, int>(x => x.WriterId);
|
||||
public readonly PropertyInfo NodeNameSelector = ExpressionHelper.GetPropertyInfo<Content, string>(x => x.NodeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template used by the Content.
|
||||
/// This is used to override the default one from the ContentType.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If no template is explicitly set on the Content object,
|
||||
/// the Default template from the ContentType will be returned.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public virtual ITemplate Template
|
||||
{
|
||||
get { return _template; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _template, Ps.Value.TemplateSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current status of the Content
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public ContentStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
if(Trashed)
|
||||
return ContentStatus.Trashed;
|
||||
|
||||
if(ExpireDate.HasValue && ExpireDate.Value > DateTime.MinValue && DateTime.Now > ExpireDate.Value)
|
||||
return ContentStatus.Expired;
|
||||
|
||||
if(ReleaseDate.HasValue && ReleaseDate.Value > DateTime.MinValue && ReleaseDate.Value > DateTime.Now)
|
||||
return ContentStatus.AwaitingRelease;
|
||||
|
||||
if(Published)
|
||||
return ContentStatus.Published;
|
||||
|
||||
return ContentStatus.Unpublished;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether this Content is Published or not
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Setting Published to true/false should be private or internal and should ONLY be used for wiring up the value
|
||||
/// from the db or modifying it based on changing the published state.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public bool Published
|
||||
{
|
||||
get { return _published; }
|
||||
internal set { SetPropertyValueAndDetectChanges(value, ref _published, Ps.Value.PublishedSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Language of the data contained within this Content object.
|
||||
/// </summary>
|
||||
[Obsolete("This is not used and will be removed from the codebase in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public string Language
|
||||
{
|
||||
get { return _language; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _language, Ps.Value.LanguageSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date this Content should be released and thus be published
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public DateTime? ReleaseDate
|
||||
{
|
||||
get { return _releaseDate; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _releaseDate, Ps.Value.ReleaseDateSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date this Content should expire and thus be unpublished
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public DateTime? ExpireDate
|
||||
{
|
||||
get { return _expireDate; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _expireDate, Ps.Value.ExpireDateSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id of the user who wrote/updated this Content
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual int WriterId
|
||||
{
|
||||
get { return _writer; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _writer, Ps.Value.WriterSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the Node (non-localized).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This Property is kept internal until localization is introduced.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
internal string NodeName
|
||||
{
|
||||
get { return _nodeName; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _nodeName, Ps.Value.NodeNameSelector); }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this content object
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public IContentType ContentType
|
||||
{
|
||||
get { return _contentType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="ContentType"/> for the current content object
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <remarks>Leaves PropertyTypes intact after change</remarks>
|
||||
public void ChangeContentType(IContentType contentType)
|
||||
{
|
||||
ContentTypeId = contentType.Id;
|
||||
_contentType = contentType;
|
||||
ContentTypeBase = contentType;
|
||||
Properties.EnsurePropertyTypes(PropertyTypes);
|
||||
Properties.CollectionChanged += PropertiesChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="ContentType"/> for the current content object and removes PropertyTypes,
|
||||
/// which are not part of the new ContentType.
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <param name="clearProperties">Boolean indicating whether to clear PropertyTypes upon change</param>
|
||||
public void ChangeContentType(IContentType contentType, bool clearProperties)
|
||||
{
|
||||
if(clearProperties)
|
||||
{
|
||||
ContentTypeId = contentType.Id;
|
||||
_contentType = contentType;
|
||||
ContentTypeBase = contentType;
|
||||
Properties.EnsureCleanPropertyTypes(PropertyTypes);
|
||||
Properties.CollectionChanged += PropertiesChanged;
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeContentType(contentType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Published state of the content object
|
||||
/// </summary>
|
||||
public void ChangePublishedState(PublishedState state)
|
||||
{
|
||||
Published = state == PublishedState.Published;
|
||||
PublishedState = state;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
internal PublishedState PublishedState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier of the published version, if any.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public Guid PublishedVersionGuid { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content has a published version.
|
||||
/// </summary>
|
||||
public bool HasPublishedVersion { get { return PublishedVersionGuid != default(Guid); } }
|
||||
|
||||
[IgnoreDataMember]
|
||||
internal DateTime PublishedDate { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public bool IsBlueprint { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Trashed state of the content object
|
||||
/// </summary>
|
||||
/// <param name="isTrashed">Boolean indicating whether content is trashed (true) or not trashed (false)</param>
|
||||
/// <param name="parentId"> </param>
|
||||
public override void ChangeTrashedState(bool isTrashed, int parentId = -20)
|
||||
{
|
||||
Trashed = isTrashed;
|
||||
ParentId = parentId;
|
||||
|
||||
//If the content is trashed and is published it should be marked as unpublished
|
||||
if (isTrashed && Published)
|
||||
{
|
||||
ChangePublishedState(PublishedState.Unpublished);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being updated
|
||||
/// </summary>
|
||||
/// <remarks>Modified Date is set and a new Version guid is set</remarks>
|
||||
internal override void UpdatingEntity()
|
||||
{
|
||||
base.UpdatingEntity();
|
||||
Version = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use DeepCloneWithResetIdentities instead")]
|
||||
public IContent Clone()
|
||||
{
|
||||
return DeepCloneWithResetIdentities();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContent DeepCloneWithResetIdentities()
|
||||
{
|
||||
var clone = (Content)DeepClone();
|
||||
clone.Key = Guid.Empty;
|
||||
clone.Version = Guid.NewGuid();
|
||||
clone.ResetIdentity();
|
||||
|
||||
foreach (var property in clone.Properties)
|
||||
{
|
||||
property.ResetIdentity();
|
||||
property.Version = clone.Version;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Content)base.DeepClone();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually clone this since it's not settable
|
||||
clone._contentType = (IContentType)ContentType.DeepClone();
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,113 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of a content variant for a given culture
|
||||
/// </summary>
|
||||
public class ContentCultureInfos : BeingDirtyBase, IDeepCloneable, IEquatable<ContentCultureInfos>
|
||||
{
|
||||
private DateTime _date;
|
||||
private string _name;
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentCultureInfos"/> class.
|
||||
/// </summary>
|
||||
public ContentCultureInfos(string culture)
|
||||
{
|
||||
if (culture.IsNullOrWhiteSpace()) throw new ArgumentNullOrEmptyException(nameof(culture));
|
||||
Culture = culture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentCultureInfos"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>Used for cloning, without change tracking.</remarks>
|
||||
internal ContentCultureInfos(ContentCultureInfos other)
|
||||
: this(other.Culture)
|
||||
{
|
||||
_name = other.Name;
|
||||
_date = other.Date;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture.
|
||||
/// </summary>
|
||||
public string Culture { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the date.
|
||||
/// </summary>
|
||||
public DateTime Date
|
||||
{
|
||||
get => _date;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _date, Ps.Value.DateSelector);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object DeepClone()
|
||||
{
|
||||
return new ContentCultureInfos(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ContentCultureInfos other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(ContentCultureInfos other)
|
||||
{
|
||||
return other != null && Culture == other.Culture && Name == other.Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = 479558943;
|
||||
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Culture);
|
||||
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructs into culture and name.
|
||||
/// </summary>
|
||||
public void Deconstruct(out string culture, out string name)
|
||||
{
|
||||
culture = Culture;
|
||||
name = Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructs into culture, name and date.
|
||||
/// </summary>
|
||||
public void Deconstruct(out string culture, out string name, out DateTime date)
|
||||
{
|
||||
Deconstruct(out culture, out name);
|
||||
date = Date;
|
||||
}
|
||||
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<ContentCultureInfos, string>(x => x.Name);
|
||||
public readonly PropertyInfo DateSelector = ExpressionHelper.GetPropertyInfo<ContentCultureInfos, DateTime>(x => x.Date);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The culture names of a content's variants
|
||||
/// </summary>
|
||||
public class ContentCultureInfosCollection : ObservableDictionary<string, ContentCultureInfos>, IDeepCloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentCultureInfosCollection"/> class.
|
||||
/// </summary>
|
||||
public ContentCultureInfosCollection()
|
||||
: base(x => x.Culture, StringComparer.InvariantCultureIgnoreCase)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentCultureInfosCollection"/> class with items.
|
||||
/// </summary>
|
||||
public ContentCultureInfosCollection(IEnumerable<ContentCultureInfos> items)
|
||||
: base(x => x.Culture, StringComparer.InvariantCultureIgnoreCase)
|
||||
{
|
||||
// make sure to add *copies* and not the original items,
|
||||
// as items can be modified by AddOrUpdate, and therefore
|
||||
// the new collection would be impacted by changes made
|
||||
// to the old collection
|
||||
foreach (var item in items)
|
||||
Add(new ContentCultureInfos(item));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates a <see cref="ContentCultureInfos"/> instance.
|
||||
/// </summary>
|
||||
public void AddOrUpdate(string culture, string name, DateTime date)
|
||||
{
|
||||
if (culture.IsNullOrWhiteSpace()) throw new ArgumentNullOrEmptyException(nameof(culture));
|
||||
culture = culture.ToLowerInvariant();
|
||||
|
||||
if (TryGetValue(culture, out var item))
|
||||
{
|
||||
item.Name = name;
|
||||
item.Date = date;
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, item));
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(new ContentCultureInfos(culture)
|
||||
{
|
||||
Name = name,
|
||||
Date = date
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = new ContentCultureInfosCollection();
|
||||
|
||||
foreach (var item in this)
|
||||
{
|
||||
var itemClone = (ContentCultureInfos) item.DeepClone();
|
||||
itemClone.ResetDirtyProperties(false);
|
||||
clone.Add(itemClone);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a content app.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Content apps are editor extensions.</para>
|
||||
/// </remarks>
|
||||
[DataContract(Name = "app", Namespace = "")]
|
||||
public class ContentApp
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the content app.
|
||||
/// </summary>
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique alias of the content app.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Must be a valid javascript identifier, ie no spaces etc.</para>
|
||||
/// </remarks>
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the weight of the content app.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Content apps are ordered by weight, from left (lowest values) to right (highest values).</para>
|
||||
/// <para>Some built-in apps have special weights: listview is -666, content is -100 and infos is +100.</para>
|
||||
/// <para>The default weight is 0, meaning somewhere in-between content and infos, but weight could
|
||||
/// be used for ordering between user-level apps, or anything really.</para>
|
||||
/// </remarks>
|
||||
[DataMember(Name = "weight")]
|
||||
public int Weight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the icon of the content app.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Must be a valid helveticons class name (see http://hlvticons.ch/).</para>
|
||||
/// </remarks>
|
||||
[DataMember(Name = "icon")]
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view for rendering the content app.
|
||||
/// </summary>
|
||||
[DataMember(Name = "view")]
|
||||
public string View { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The view model specific to this app
|
||||
/// </summary>
|
||||
[DataMember(Name = "viewModel")]
|
||||
public object ViewModel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the app is active.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Normally reserved for Angular to deal with but in some cases this can be set on the server side.</para>
|
||||
/// </remarks>
|
||||
[DataMember(Name = "active")]
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a content app definition.
|
||||
/// </summary>
|
||||
public interface IContentAppDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the content app for an object.
|
||||
/// </summary>
|
||||
/// <param name="source">The source object.</param>
|
||||
/// <returns>The content app for the object, or null.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The definition must determine, based on <paramref name="source"/>, whether
|
||||
/// the content app should be displayed or not, and return either a <see cref="ContentApp"/>
|
||||
/// instance, or null.</para>
|
||||
/// </remarks>
|
||||
ContentApp GetContentAppFor(object source, IEnumerable<IReadOnlyUserGroup> userGroups);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,863 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public static class ContentExtensions
|
||||
{
|
||||
#region IContent
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this entity was just published as part of a recent save operation (i.e. it wasn't previously published)
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is helpful for determining if the published event will execute during the saved event for a content item.
|
||||
/// </remarks>
|
||||
internal static bool JustPublished(this IContent entity)
|
||||
{
|
||||
var dirty = (IRememberBeingDirty)entity;
|
||||
return dirty.WasPropertyDirty("Published") && entity.Published;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the item should be persisted at all
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// In one particular case, a content item shouldn't be persisted:
|
||||
/// * The item exists and is published
|
||||
/// * A call to ContentService.Save is made
|
||||
/// * The item has not been modified whatsoever apart from changing it's published status from published to saved
|
||||
///
|
||||
/// In this case, there is no reason to make any database changes at all
|
||||
/// </remarks>
|
||||
internal static bool RequiresSaving(this IContent entity)
|
||||
{
|
||||
var publishedState = ((Content)entity).PublishedState;
|
||||
return RequiresSaving(entity, publishedState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the item should be persisted at all
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="publishedState"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// In one particular case, a content item shouldn't be persisted:
|
||||
/// * The item exists and is published
|
||||
/// * A call to ContentService.Save is made
|
||||
/// * The item has not been modified whatsoever apart from changing it's published status from published to saved
|
||||
///
|
||||
/// In this case, there is no reason to make any database changes at all
|
||||
/// </remarks>
|
||||
internal static bool RequiresSaving(this IContent entity, PublishedState publishedState)
|
||||
{
|
||||
var publishedChanged = entity.IsPropertyDirty("Published") && publishedState != PublishedState.Unpublished;
|
||||
//check if any user prop has changed
|
||||
var propertyValueChanged = entity.IsAnyUserPropertyDirty();
|
||||
|
||||
//We need to know if any other property apart from Published was changed here
|
||||
//don't create a new version if the published state has changed to 'Save' but no data has actually been changed
|
||||
if (publishedChanged && entity.Published == false && propertyValueChanged == false)
|
||||
{
|
||||
//at this point we need to check if any non property value has changed that wasn't the published state
|
||||
var changedProps = ((TracksChangesEntityBase)entity).GetDirtyProperties();
|
||||
if (changedProps.Any(x => x != "Published") == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a new version should be created
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// A new version needs to be created when:
|
||||
/// * The publish status is changed
|
||||
/// * The language is changed
|
||||
/// * The item is already published and is being published again and any property value is changed (to enable a rollback)
|
||||
/// </remarks>
|
||||
internal static bool ShouldCreateNewVersion(this IContent entity)
|
||||
{
|
||||
var publishedState = ((Content)entity).PublishedState;
|
||||
return ShouldCreateNewVersion(entity, publishedState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all dirty user defined properties
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> GetDirtyUserProperties(this IContentBase entity)
|
||||
{
|
||||
return entity.Properties.Where(x => x.IsDirty()).Select(x => x.Alias);
|
||||
}
|
||||
|
||||
public static bool IsAnyUserPropertyDirty(this IContentBase entity)
|
||||
{
|
||||
return entity.Properties.Any(x => x.IsDirty());
|
||||
}
|
||||
|
||||
public static bool WasAnyUserPropertyDirty(this IContentBase entity)
|
||||
{
|
||||
return entity.Properties.Any(x => x.WasDirty());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a new version should be created
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="publishedState"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// A new version needs to be created when:
|
||||
/// * The publish status is changed
|
||||
/// * The language is changed
|
||||
/// * The item is already published and is being published again and any property value is changed (to enable a rollback)
|
||||
/// </remarks>
|
||||
internal static bool ShouldCreateNewVersion(this IContent entity, PublishedState publishedState)
|
||||
{
|
||||
//check if the published state has changed or the language
|
||||
var publishedChanged = entity.IsPropertyDirty("Published") && publishedState != PublishedState.Unpublished;
|
||||
var langChanged = entity.IsPropertyDirty("Language");
|
||||
var contentChanged = publishedChanged || langChanged;
|
||||
|
||||
//check if any user prop has changed
|
||||
var propertyValueChanged = entity.IsAnyUserPropertyDirty();
|
||||
|
||||
//return true if published or language has changed
|
||||
if (contentChanged)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//check if any content prop has changed
|
||||
var contentDataChanged = ((Content)entity).IsEntityDirty();
|
||||
|
||||
//return true if the item is published and a property has changed or if any content property has changed
|
||||
return (propertyValueChanged && publishedState == PublishedState.Published) || contentDataChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the published db flag should be set to true for the current entity version and all other db
|
||||
/// versions should have their flag set to false.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is determined by:
|
||||
/// * If a new version is being created and the entity is published
|
||||
/// * If the published state has changed and the entity is published OR the entity has been un-published.
|
||||
/// </remarks>
|
||||
internal static bool ShouldClearPublishedFlagForPreviousVersions(this IContent entity)
|
||||
{
|
||||
var publishedState = ((Content)entity).PublishedState;
|
||||
return entity.ShouldClearPublishedFlagForPreviousVersions(publishedState, entity.ShouldCreateNewVersion(publishedState));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the published db flag should be set to true for the current entity version and all other db
|
||||
/// versions should have their flag set to false.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="publishedState"></param>
|
||||
/// <param name="isCreatingNewVersion"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is determined by:
|
||||
/// * If a new version is being created and the entity is published
|
||||
/// * If the published state has changed and the entity is published OR the entity has been un-published.
|
||||
/// </remarks>
|
||||
internal static bool ShouldClearPublishedFlagForPreviousVersions(this IContent entity, PublishedState publishedState, bool isCreatingNewVersion)
|
||||
{
|
||||
if (isCreatingNewVersion && entity.Published)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//If Published state has changed then previous versions should have their publish state reset.
|
||||
//If state has been changed to unpublished the previous versions publish state should also be reset.
|
||||
if (entity.IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of the current contents ancestors, not including the content itself.
|
||||
/// </summary>
|
||||
/// <param name="content">Current content</param>
|
||||
/// <returns>An enumerable list of <see cref="IContent"/> objects</returns>
|
||||
public static IEnumerable<IContent> Ancestors(this IContent content)
|
||||
{
|
||||
return ApplicationContext.Current.Services.ContentService.GetAncestors(content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of the current contents children.
|
||||
/// </summary>
|
||||
/// <param name="content">Current content</param>
|
||||
/// <returns>An enumerable list of <see cref="IContent"/> objects</returns>
|
||||
public static IEnumerable<IContent> Children(this IContent content)
|
||||
{
|
||||
return ApplicationContext.Current.Services.ContentService.GetChildren(content.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of the current contents descendants, not including the content itself.
|
||||
/// </summary>
|
||||
/// <param name="content">Current content</param>
|
||||
/// <returns>An enumerable list of <see cref="IContent"/> objects</returns>
|
||||
public static IEnumerable<IContent> Descendants(this IContent content)
|
||||
{
|
||||
return ApplicationContext.Current.Services.ContentService.GetDescendants(content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the parent of the current content.
|
||||
/// </summary>
|
||||
/// <param name="content">Current content</param>
|
||||
/// <returns>An <see cref="IContent"/> object</returns>
|
||||
public static IContent Parent(this IContent content)
|
||||
{
|
||||
return ApplicationContext.Current.Services.ContentService.GetById(content.ParentId);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IMedia
|
||||
/// <summary>
|
||||
/// Returns a list of the current medias ancestors, not including the media itself.
|
||||
/// </summary>
|
||||
/// <param name="media">Current media</param>
|
||||
/// <returns>An enumerable list of <see cref="IMedia"/> objects</returns>
|
||||
public static IEnumerable<IMedia> Ancestors(this IMedia media)
|
||||
{
|
||||
return ApplicationContext.Current.Services.MediaService.GetAncestors(media);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of the current medias children.
|
||||
/// </summary>
|
||||
/// <param name="media">Current media</param>
|
||||
/// <returns>An enumerable list of <see cref="IMedia"/> objects</returns>
|
||||
public static IEnumerable<IMedia> Children(this IMedia media)
|
||||
{
|
||||
return ApplicationContext.Current.Services.MediaService.GetChildren(media.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of the current medias descendants, not including the media itself.
|
||||
/// </summary>
|
||||
/// <param name="media">Current media</param>
|
||||
/// <returns>An enumerable list of <see cref="IMedia"/> objects</returns>
|
||||
public static IEnumerable<IMedia> Descendants(this IMedia media)
|
||||
{
|
||||
return ApplicationContext.Current.Services.MediaService.GetDescendants(media);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the parent of the current media.
|
||||
/// </summary>
|
||||
/// <param name="media">Current media</param>
|
||||
/// <returns>An <see cref="IMedia"/> object</returns>
|
||||
public static IMedia Parent(this IMedia media)
|
||||
{
|
||||
return ApplicationContext.Current.Services.MediaService.GetById(media.ParentId);
|
||||
}
|
||||
#endregion
|
||||
|
||||
internal static bool IsInRecycleBin(this IContent content)
|
||||
{
|
||||
return IsInRecycleBin(content, Constants.System.RecycleBinContent);
|
||||
}
|
||||
|
||||
internal static bool IsInRecycleBin(this IMedia media)
|
||||
{
|
||||
return IsInRecycleBin(media, Constants.System.RecycleBinMedia);
|
||||
}
|
||||
|
||||
internal static bool IsInRecycleBin(this IContentBase content, int recycleBinId)
|
||||
{
|
||||
return content.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Contains(recycleBinId.ToInvariantString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes characters that are not valide XML characters from all entity properties
|
||||
/// of type string. See: http://stackoverflow.com/a/961504/5018
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If this is not done then the xml cache can get corrupt and it will throw YSODs upon reading it.
|
||||
/// </remarks>
|
||||
/// <param name="entity"></param>
|
||||
public static void SanitizeEntityPropertiesForXmlStorage(this IContentBase entity)
|
||||
{
|
||||
entity.Name = entity.Name.ToValidXmlString();
|
||||
foreach (var property in entity.Properties)
|
||||
{
|
||||
if (property.Value is string)
|
||||
{
|
||||
var value = (string) property.Value;
|
||||
property.Value = value.ToValidXmlString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the IContentBase has children
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is a bit of a hack because we need to type check!
|
||||
/// </remarks>
|
||||
internal static bool HasChildren(IContentBase content, ServiceContext services)
|
||||
{
|
||||
if (content is IContent)
|
||||
{
|
||||
return services.ContentService.HasChildren(content.Id);
|
||||
}
|
||||
if (content is IMedia)
|
||||
{
|
||||
return services.MediaService.HasChildren(content.Id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the children for the content base item
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is a bit of a hack because we need to type check!
|
||||
/// </remarks>
|
||||
internal static IEnumerable<IContentBase> Children(IContentBase content, ServiceContext services)
|
||||
{
|
||||
if (content is IContent)
|
||||
{
|
||||
return services.ContentService.GetChildren(content.Id);
|
||||
}
|
||||
if (content is IMedia)
|
||||
{
|
||||
return services.MediaService.GetChildren(content.Id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns properties that do not belong to a group
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Property> GetNonGroupedProperties(this IContentBase content)
|
||||
{
|
||||
var propertyIdsInTabs = content.PropertyGroups.SelectMany(pg => pg.PropertyTypes);
|
||||
return content.Properties
|
||||
.Where(property => propertyIdsInTabs.Contains(property.PropertyType) == false)
|
||||
.OrderBy(x => x.PropertyType.SortOrder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Property object for the given property group
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="propertyGroup"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Property> GetPropertiesForGroup(this IContentBase content, PropertyGroup propertyGroup)
|
||||
{
|
||||
//get the properties for the current tab
|
||||
return content.Properties
|
||||
.Where(property => propertyGroup.PropertyTypes
|
||||
.Select(propertyType => propertyType.Id)
|
||||
.Contains(property.PropertyTypeId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set property values by alias with an annonymous object
|
||||
/// </summary>
|
||||
public static void PropertyValues(this IContentBase content, object value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new Exception("No properties has been passed in");
|
||||
|
||||
var propertyInfos = value.GetType().GetProperties();
|
||||
foreach (var propertyInfo in propertyInfos)
|
||||
{
|
||||
//Check if a PropertyType with alias exists thus being a valid property
|
||||
var propertyType = content.PropertyTypes.FirstOrDefault(x => x.Alias == propertyInfo.Name);
|
||||
if (propertyType == null)
|
||||
throw new Exception(
|
||||
string.Format(
|
||||
"The property alias {0} is not valid, because no PropertyType with this alias exists",
|
||||
propertyInfo.Name));
|
||||
|
||||
//Check if a Property with the alias already exists in the collection thus being updated or inserted
|
||||
var item = content.Properties.FirstOrDefault(x => x.Alias == propertyInfo.Name);
|
||||
if (item != null)
|
||||
{
|
||||
item.Value = propertyInfo.GetValue(value, null);
|
||||
//Update item with newly added value
|
||||
content.Properties.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Create new Property to add to collection
|
||||
var property = propertyType.CreatePropertyFromValue(propertyInfo.GetValue(value, null));
|
||||
content.Properties.Add(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IContentTypeComposition GetContentType(this IContentBase contentBase)
|
||||
{
|
||||
if (contentBase == null) throw new ArgumentNullException("contentBase");
|
||||
|
||||
var content = contentBase as IContent;
|
||||
if (content != null) return content.ContentType;
|
||||
var media = contentBase as IMedia;
|
||||
if (media != null) return media.ContentType;
|
||||
var member = contentBase as IMember;
|
||||
if (member != null) return member.ContentType;
|
||||
throw new NotSupportedException("Unsupported IContentBase implementation: " + contentBase.GetType().FullName + ".");
|
||||
}
|
||||
|
||||
#region SetValue for setting file contents
|
||||
|
||||
/// <summary>
|
||||
/// Stores and sets an uploaded HttpPostedFileBase as a property value.
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
|
||||
/// <param name="propertyTypeAlias">The property alias.</param>
|
||||
/// <param name="value">The uploaded <see cref="HttpPostedFileBase"/>.</param>
|
||||
public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value)
|
||||
{
|
||||
// ensure we get the filename without the path in IE in intranet mode
|
||||
// http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie
|
||||
var filename = value.FileName;
|
||||
var pos = filename.LastIndexOf(@"\", StringComparison.InvariantCulture);
|
||||
if (pos > 0)
|
||||
filename = filename.Substring(pos + 1);
|
||||
|
||||
// strip any directory info
|
||||
pos = filename.LastIndexOf(IOHelper.DirSepChar);
|
||||
if (pos > 0)
|
||||
filename = filename.Substring(pos + 1);
|
||||
|
||||
// get a safe & clean filename
|
||||
filename = IOHelper.SafeFileName(filename);
|
||||
if (string.IsNullOrWhiteSpace(filename)) return;
|
||||
filename = filename.ToLower();
|
||||
|
||||
FileSystemProviderManager.Current.MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, value.InputStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores and sets an uploaded HttpPostedFile as a property value.
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
|
||||
/// <param name="propertyTypeAlias">The property alias.</param>
|
||||
/// <param name="value">The uploaded <see cref="HttpPostedFile"/>.</param>
|
||||
public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFile value)
|
||||
{
|
||||
SetValue(content, propertyTypeAlias, (HttpPostedFileBase) new HttpPostedFileWrapper(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores and sets an uploaded HttpPostedFileWrapper as a property value.
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
|
||||
/// <param name="propertyTypeAlias">The property alias.</param>
|
||||
/// <param name="value">The uploaded <see cref="HttpPostedFileWrapper"/>.</param>
|
||||
[Obsolete("There is no reason for this overload since HttpPostedFileWrapper inherits from HttpPostedFileBase")]
|
||||
public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileWrapper value)
|
||||
{
|
||||
SetValue(content, propertyTypeAlias, (HttpPostedFileBase) value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores and sets a file as a property value.
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
|
||||
/// <param name="propertyTypeAlias">The property alias.</param>
|
||||
/// <param name="filename">The name of the file.</param>
|
||||
/// <param name="filestream">A stream containing the file data.</param>
|
||||
/// <remarks>This really is for FileUpload fields only, and should be obsoleted. For anything else,
|
||||
/// you need to store the file by yourself using Store and then figure out
|
||||
/// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself.</remarks>
|
||||
public static void SetValue(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream)
|
||||
{
|
||||
if (filename == null || filestream == null) return;
|
||||
|
||||
// get a safe & clean filename
|
||||
filename = IOHelper.SafeFileName(filename);
|
||||
if (string.IsNullOrWhiteSpace(filename)) return;
|
||||
filename = filename.ToLower();
|
||||
|
||||
FileSystemProviderManager.Current.MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, filestream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a file.
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
|
||||
/// <param name="propertyTypeAlias">The property alias.</param>
|
||||
/// <param name="filename">The name of the file.</param>
|
||||
/// <param name="filestream">A stream containing the file data.</param>
|
||||
/// <param name="filepath">The original file path, if any.</param>
|
||||
/// <returns>The path to the file, relative to the media filesystem.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Does NOT set the property value, so one should probably store the file and then do
|
||||
/// something alike: property.Value = MediaHelper.FileSystem.GetUrl(filepath).</para>
|
||||
/// <para>The original file path is used, in the old media file path scheme, to try and reuse
|
||||
/// the "folder number" that was assigned to the previous file referenced by the property,
|
||||
/// if any.</para>
|
||||
/// </remarks>
|
||||
public static string StoreFile(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string filepath)
|
||||
{
|
||||
var propertyType = content.GetContentType()
|
||||
.CompositionPropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias));
|
||||
if (propertyType == null) throw new ArgumentException("Invalid property type alias " + propertyTypeAlias + ".");
|
||||
return FileSystemProviderManager.Current.MediaFileSystem.StoreFile(content, propertyType, filename, filestream, filepath);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region User/Profile methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IProfile"/> for the Creator of this media item.
|
||||
/// </summary>
|
||||
[Obsolete("Use the overload that declares the IUserService to use")]
|
||||
public static IProfile GetCreatorProfile(this IMedia media)
|
||||
{
|
||||
return ApplicationContext.Current.Services.UserService.GetProfileById(media.CreatorId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IProfile"/> for the Creator of this media item.
|
||||
/// </summary>
|
||||
public static IProfile GetCreatorProfile(this IMedia media, IUserService userService)
|
||||
{
|
||||
return userService.GetProfileById(media.CreatorId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IProfile"/> for the Creator of this content item.
|
||||
/// </summary>
|
||||
[Obsolete("Use the overload that declares the IUserService to use")]
|
||||
public static IProfile GetCreatorProfile(this IContentBase content)
|
||||
{
|
||||
return ApplicationContext.Current.Services.UserService.GetProfileById(content.CreatorId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IProfile"/> for the Creator of this content item.
|
||||
/// </summary>
|
||||
public static IProfile GetCreatorProfile(this IContentBase content, IUserService userService)
|
||||
{
|
||||
return userService.GetProfileById(content.CreatorId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IProfile"/> for the Writer of this content.
|
||||
/// </summary>
|
||||
[Obsolete("Use the overload that declares the IUserService to use")]
|
||||
public static IProfile GetWriterProfile(this IContent content)
|
||||
{
|
||||
return ApplicationContext.Current.Services.UserService.GetProfileById(content.WriterId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IProfile"/> for the Writer of this content.
|
||||
/// </summary>
|
||||
public static IProfile GetWriterProfile(this IContent content, IUserService userService)
|
||||
{
|
||||
return userService.GetProfileById(content.WriterId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an <see cref="IContent"/> item has any published versions
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <returns>True if the content has any published versiom otherwise False</returns>
|
||||
[Obsolete("Use the HasPublishedVersion property.", false)]
|
||||
public static bool HasPublishedVersion(this IContent content)
|
||||
{
|
||||
return content.HasPublishedVersion;
|
||||
}
|
||||
|
||||
#region Tag methods
|
||||
|
||||
///// <summary>
|
||||
///// Returns the tags for the given property
|
||||
///// </summary>
|
||||
///// <param name="content"></param>
|
||||
///// <param name="propertyTypeAlias"></param>
|
||||
///// <param name="tagGroup"></param>
|
||||
///// <returns></returns>
|
||||
///// <remarks>
|
||||
///// The tags returned are only relavent for published content & saved media or members
|
||||
///// </remarks>
|
||||
//public static IEnumerable<ITag> GetTags(this IContentBase content, string propertyTypeAlias, string tagGroup = "default")
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags.
|
||||
/// </summary>
|
||||
/// <param name="content">The content item to assign the tags to</param>
|
||||
/// <param name="propertyTypeAlias">The property alias to assign the tags to</param>
|
||||
/// <param name="tags">The tags to assign</param>
|
||||
/// <param name="replaceTags">True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones</param>
|
||||
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
|
||||
/// <returns></returns>
|
||||
public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
|
||||
{
|
||||
content.SetTags(TagCacheStorageType.Csv, propertyTypeAlias, tags, replaceTags, tagGroup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags.
|
||||
/// </summary>
|
||||
/// <param name="content">The content item to assign the tags to</param>
|
||||
/// <param name="storageType">The tag storage type in cache (default is csv)</param>
|
||||
/// <param name="propertyTypeAlias">The property alias to assign the tags to</param>
|
||||
/// <param name="tags">The tags to assign</param>
|
||||
/// <param name="replaceTags">True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones</param>
|
||||
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
|
||||
/// <returns></returns>
|
||||
public static void SetTags(this IContentBase content, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
|
||||
{
|
||||
var property = content.Properties[propertyTypeAlias];
|
||||
if (property == null)
|
||||
{
|
||||
throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias);
|
||||
}
|
||||
property.SetTags(storageType, propertyTypeAlias, tags, replaceTags, tagGroup);
|
||||
}
|
||||
|
||||
internal static void SetTags(this Property property, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
|
||||
{
|
||||
if (property == null) throw new ArgumentNullException("property");
|
||||
|
||||
var trimmedTags = tags.Select(x => x.Trim()).ToArray();
|
||||
|
||||
property.TagSupport.Enable = true;
|
||||
property.TagSupport.Tags = trimmedTags.Select(x => new Tuple<string, string>(x, tagGroup));
|
||||
property.TagSupport.Behavior = replaceTags ? PropertyTagBehavior.Replace : PropertyTagBehavior.Merge;
|
||||
|
||||
//ensure the property value is set to the same thing
|
||||
if (replaceTags)
|
||||
{
|
||||
switch (storageType)
|
||||
{
|
||||
case TagCacheStorageType.Csv:
|
||||
property.Value = string.Join(",", trimmedTags);
|
||||
break;
|
||||
case TagCacheStorageType.Json:
|
||||
//json array
|
||||
property.Value = JsonConvert.SerializeObject(trimmedTags);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (storageType)
|
||||
{
|
||||
case TagCacheStorageType.Csv:
|
||||
var currTags = property.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim());
|
||||
property.Value = string.Join(",", trimmedTags.Union(currTags));
|
||||
break;
|
||||
case TagCacheStorageType.Json:
|
||||
var currJson = JsonConvert.DeserializeObject<JArray>(property.Value.ToString());
|
||||
//need to append the new ones
|
||||
foreach (var tag in trimmedTags)
|
||||
{
|
||||
currJson.Add(tag);
|
||||
}
|
||||
//json array
|
||||
property.Value = JsonConvert.SerializeObject(currJson);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove any of the tags specified in the collection from the property if they are currently assigned.
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="propertyTypeAlias"></param>
|
||||
/// <param name="tags"></param>
|
||||
/// <param name="tagGroup">The group/category that the tags are currently assigned to, the default value is "default"</param>
|
||||
public static void RemoveTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, string tagGroup = "default")
|
||||
{
|
||||
var property = content.Properties[propertyTypeAlias];
|
||||
if (property == null)
|
||||
{
|
||||
throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias);
|
||||
}
|
||||
|
||||
var trimmedTags = tags.Select(x => x.Trim()).ToArray();
|
||||
|
||||
property.TagSupport.Behavior = PropertyTagBehavior.Remove;
|
||||
property.TagSupport.Enable = true;
|
||||
property.TagSupport.Tags = trimmedTags.Select(x => new Tuple<string, string>(x, tagGroup));
|
||||
|
||||
//set the property value
|
||||
var currTags = property.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim());
|
||||
|
||||
property.Value = string.Join(",", currTags.Except(trimmedTags));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region XML methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates the full xml representation for the <see cref="IContent"/> object and all of it's descendants
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContent"/> to generate xml for</param>
|
||||
/// <param name="packagingService"></param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
internal static XElement ToDeepXml(this IContent content, IPackagingService packagingService)
|
||||
{
|
||||
return packagingService.Export(content, true, raiseEvents: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IContent"/> object
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContent"/> to generate xml for</param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
[Obsolete("Use the overload that declares the IPackagingService to use")]
|
||||
public static XElement ToXml(this IContent content)
|
||||
{
|
||||
return ApplicationContext.Current.Services.PackagingService.Export(content, raiseEvents: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IContent"/> object
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContent"/> to generate xml for</param>
|
||||
/// <param name="packagingService"></param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
public static XElement ToXml(this IContent content, IPackagingService packagingService)
|
||||
{
|
||||
return packagingService.Export(content, raiseEvents: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IMedia"/> object
|
||||
/// </summary>
|
||||
/// <param name="media"><see cref="IContent"/> to generate xml for</param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
[Obsolete("Use the overload that declares the IPackagingService to use")]
|
||||
public static XElement ToXml(this IMedia media)
|
||||
{
|
||||
return ApplicationContext.Current.Services.PackagingService.Export(media, raiseEvents: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IMedia"/> object
|
||||
/// </summary>
|
||||
/// <param name="media"><see cref="IContent"/> to generate xml for</param>
|
||||
/// <param name="packagingService"></param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
public static XElement ToXml(this IMedia media, IPackagingService packagingService)
|
||||
{
|
||||
return packagingService.Export(media, raiseEvents: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the full xml representation for the <see cref="IMedia"/> object and all of it's descendants
|
||||
/// </summary>
|
||||
/// <param name="media"><see cref="IMedia"/> to generate xml for</param>
|
||||
/// <param name="packagingService"></param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IMedia"/></returns>
|
||||
internal static XElement ToDeepXml(this IMedia media, IPackagingService packagingService)
|
||||
{
|
||||
return packagingService.Export(media, true, raiseEvents: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IContent"/> object
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContent"/> to generate xml for</param>
|
||||
/// <param name="isPreview">Boolean indicating whether the xml should be generated for preview</param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
[Obsolete("Use the overload that declares the IPackagingService to use")]
|
||||
public static XElement ToXml(this IContent content, bool isPreview)
|
||||
{
|
||||
//TODO Do a proper implementation of this
|
||||
//If current IContent is published we should get latest unpublished version
|
||||
return content.ToXml();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IContent"/> object
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContent"/> to generate xml for</param>
|
||||
/// <param name="packagingService"></param>
|
||||
/// <param name="isPreview">Boolean indicating whether the xml should be generated for preview</param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
public static XElement ToXml(this IContent content, IPackagingService packagingService, bool isPreview)
|
||||
{
|
||||
//TODO Do a proper implementation of this
|
||||
//If current IContent is published we should get latest unpublished version
|
||||
return content.ToXml(packagingService);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IMember"/> object
|
||||
/// </summary>
|
||||
/// <param name="member"><see cref="IMember"/> to generate xml for</param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
[Obsolete("Use the overload that declares the IPackagingService to use")]
|
||||
public static XElement ToXml(this IMember member)
|
||||
{
|
||||
return ((PackagingService)(ApplicationContext.Current.Services.PackagingService)).Export(member);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IMember"/> object
|
||||
/// </summary>
|
||||
/// <param name="member"><see cref="IMember"/> to generate xml for</param>
|
||||
/// <param name="packagingService"></param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
public static XElement ToXml(this IMember member, IPackagingService packagingService)
|
||||
{
|
||||
return ((PackagingService)(packagingService)).Export(member);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used content repository in order to add an entity to the persisted collection to be saved
|
||||
/// in a single transaction during saving an entity
|
||||
/// </summary>
|
||||
internal class ContentPreviewEntity<TContent> : ContentXmlEntity<TContent>
|
||||
where TContent : IContentBase
|
||||
{
|
||||
public ContentPreviewEntity(TContent content, Func<TContent, XElement> xml)
|
||||
: base(content, xml)
|
||||
{
|
||||
}
|
||||
|
||||
public Guid Version
|
||||
{
|
||||
get { return Content.Version; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a scheduled action for a document.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class ContentSchedule : IDeepCloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentSchedule"/> class.
|
||||
/// </summary>
|
||||
public ContentSchedule(string culture, DateTime date, ContentScheduleAction action)
|
||||
{
|
||||
Id = Guid.Empty; // will be assigned by document repository
|
||||
Culture = culture;
|
||||
Date = date;
|
||||
Action = action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentSchedule"/> class.
|
||||
/// </summary>
|
||||
public ContentSchedule(Guid id, string culture, DateTime date, ContentScheduleAction action)
|
||||
{
|
||||
Id = id;
|
||||
Culture = culture;
|
||||
Date = date;
|
||||
Action = action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the document targeted by the scheduled action.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public Guid Id { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture of the scheduled action.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// string.Empty represents the invariant culture.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public string Culture { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the date of the scheduled action.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public DateTime Date { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the action to take.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public ContentScheduleAction Action { get; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is ContentSchedule other && Equals(other);
|
||||
|
||||
public bool Equals(ContentSchedule other)
|
||||
{
|
||||
// don't compare Ids, two ContentSchedule are equal if they are for the same change
|
||||
// for the same culture, on the same date - and the collection deals w/duplicates
|
||||
return Culture.InvariantEquals(other.Culture) && Date == other.Date && Action == other.Action;
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
return new ContentSchedule(Id, Culture, Date, Action);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines scheduled actions for documents.
|
||||
/// </summary>
|
||||
public enum ContentScheduleAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Release the document.
|
||||
/// </summary>
|
||||
Release,
|
||||
|
||||
/// <summary>
|
||||
/// Expire the document.
|
||||
/// </summary>
|
||||
Expire
|
||||
}
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public class ContentScheduleCollection : INotifyCollectionChanged, IDeepCloneable, IEquatable<ContentScheduleCollection>
|
||||
{
|
||||
//underlying storage for the collection backed by a sorted list so that the schedule is always in order of date and that duplicate dates per culture are not allowed
|
||||
private readonly Dictionary<string, SortedList<DateTime, ContentSchedule>> _schedule
|
||||
= new Dictionary<string, SortedList<DateTime, ContentSchedule>>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
CollectionChanged?.Invoke(this, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an existing schedule
|
||||
/// </summary>
|
||||
/// <param name="schedule"></param>
|
||||
public void Add(ContentSchedule schedule)
|
||||
{
|
||||
if (!_schedule.TryGetValue(schedule.Culture, out var changes))
|
||||
{
|
||||
changes = new SortedList<DateTime, ContentSchedule>();
|
||||
_schedule[schedule.Culture] = changes;
|
||||
}
|
||||
|
||||
//TODO: Below will throw if there are duplicate dates added, validate/return bool?
|
||||
changes.Add(schedule.Date, schedule);
|
||||
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, schedule));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new schedule for invariant content
|
||||
/// </summary>
|
||||
/// <param name="releaseDate"></param>
|
||||
/// <param name="expireDate"></param>
|
||||
public bool Add(DateTime? releaseDate, DateTime? expireDate)
|
||||
{
|
||||
return Add(string.Empty, releaseDate, expireDate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new schedule for a culture
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <param name="releaseDate"></param>
|
||||
/// <param name="expireDate"></param>
|
||||
/// <returns>true if successfully added, false if validation fails</returns>
|
||||
public bool Add(string culture, DateTime? releaseDate, DateTime? expireDate)
|
||||
{
|
||||
if (culture == null) throw new ArgumentNullException(nameof(culture));
|
||||
if (releaseDate.HasValue && expireDate.HasValue && releaseDate >= expireDate)
|
||||
return false;
|
||||
|
||||
if (!releaseDate.HasValue && !expireDate.HasValue) return false;
|
||||
|
||||
//TODO: Do we allow passing in a release or expiry date that is before now?
|
||||
|
||||
if (!_schedule.TryGetValue(culture, out var changes))
|
||||
{
|
||||
changes = new SortedList<DateTime, ContentSchedule>();
|
||||
_schedule[culture] = changes;
|
||||
}
|
||||
|
||||
//TODO: Below will throw if there are duplicate dates added, should validate/return bool?
|
||||
// but the bool won't indicate which date was in error, maybe have 2 diff methods to schedule start/end?
|
||||
|
||||
if (releaseDate.HasValue)
|
||||
{
|
||||
var entry = new ContentSchedule(culture, releaseDate.Value, ContentScheduleAction.Release);
|
||||
changes.Add(releaseDate.Value, entry);
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, entry));
|
||||
}
|
||||
|
||||
if (expireDate.HasValue)
|
||||
{
|
||||
var entry = new ContentSchedule(culture, expireDate.Value, ContentScheduleAction.Expire);
|
||||
changes.Add(expireDate.Value, entry);
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, entry));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a scheduled change
|
||||
/// </summary>
|
||||
/// <param name="change"></param>
|
||||
public void Remove(ContentSchedule change)
|
||||
{
|
||||
if (_schedule.TryGetValue(change.Culture, out var s))
|
||||
{
|
||||
var removed = s.Remove(change.Date);
|
||||
if (removed)
|
||||
{
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, change));
|
||||
if (s.Count == 0)
|
||||
_schedule.Remove(change.Culture);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all of the scheduled change type for invariant content
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="changeDate">If specified, will clear all entries with dates less than or equal to the value</param>
|
||||
public void Clear(ContentScheduleAction action, DateTime? changeDate = null)
|
||||
{
|
||||
Clear(string.Empty, action, changeDate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all of the scheduled change type for the culture
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="date">If specified, will clear all entries with dates less than or equal to the value</param>
|
||||
public void Clear(string culture, ContentScheduleAction action, DateTime? date = null)
|
||||
{
|
||||
if (!_schedule.TryGetValue(culture, out var schedules))
|
||||
return;
|
||||
|
||||
var removes = schedules.Where(x => x.Value.Action == action && (!date.HasValue || x.Value.Date <= date.Value)).ToList();
|
||||
|
||||
foreach (var remove in removes)
|
||||
{
|
||||
var removed = schedules.Remove(remove.Value.Date);
|
||||
if (!removed)
|
||||
continue;
|
||||
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, remove.Value));
|
||||
}
|
||||
|
||||
if (schedules.Count == 0)
|
||||
_schedule.Remove(culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all pending schedules based on the date and type provided
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="date"></param>
|
||||
/// <returns></returns>
|
||||
public IReadOnlyList<ContentSchedule> GetPending(ContentScheduleAction action, DateTime date)
|
||||
{
|
||||
return _schedule.Values.SelectMany(x => x.Values).Where(x => x.Date <= date).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the schedule for invariant content
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<ContentSchedule> GetSchedule(ContentScheduleAction? action = null)
|
||||
{
|
||||
return GetSchedule(string.Empty, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the schedule for a culture
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<ContentSchedule> GetSchedule(string culture, ContentScheduleAction? action = null)
|
||||
{
|
||||
if (_schedule.TryGetValue(culture, out var changes))
|
||||
return action == null ? changes.Values : changes.Values.Where(x => x.Action == action.Value);
|
||||
return Enumerable.Empty<ContentSchedule>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all schedules registered
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IReadOnlyList<ContentSchedule> FullSchedule => _schedule.SelectMany(x => x.Value.Values).ToList();
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = new ContentScheduleCollection();
|
||||
foreach(var cultureSched in _schedule)
|
||||
{
|
||||
var list = new SortedList<DateTime, ContentSchedule>();
|
||||
foreach (var schedEntry in cultureSched.Value)
|
||||
list.Add(schedEntry.Key, (ContentSchedule)schedEntry.Value.DeepClone());
|
||||
clone._schedule[cultureSched.Key] = list;
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is ContentScheduleCollection other && Equals(other);
|
||||
|
||||
public bool Equals(ContentScheduleCollection other)
|
||||
{
|
||||
if (other == null) return false;
|
||||
|
||||
var thisSched = _schedule;
|
||||
var thatSched = other._schedule;
|
||||
|
||||
if (thisSched.Count != thatSched.Count)
|
||||
return false;
|
||||
|
||||
foreach (var (culture, thisList) in thisSched)
|
||||
{
|
||||
// if culture is missing, or actions differ, false
|
||||
if (!thatSched.TryGetValue(culture, out var thatList) || !thatList.SequenceEqual(thisList))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the states of a document, with regard to (schedule) publishing.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract]
|
||||
public enum ContentStatus
|
||||
{
|
||||
// typical flow:
|
||||
// Unpublished (add release date)-> AwaitingRelease (release)-> Published (expire)-> Expired
|
||||
|
||||
/// <summary>
|
||||
/// The document is not trashed, and not published.
|
||||
/// </summary>
|
||||
[EnumMember]
|
||||
Unpublished,
|
||||
|
||||
/// <summary>
|
||||
/// The document is published.
|
||||
/// </summary>
|
||||
[EnumMember]
|
||||
Published,
|
||||
|
||||
/// <summary>
|
||||
/// The document is not trashed, not published, after being unpublished by a scheduled action.
|
||||
/// </summary>
|
||||
[EnumMember]
|
||||
Expired,
|
||||
|
||||
/// <summary>
|
||||
/// The document is trashed.
|
||||
/// </summary>
|
||||
[EnumMember]
|
||||
Trashed,
|
||||
|
||||
/// <summary>
|
||||
/// The document is not trashed, not published, and pending publication by a scheduled action.
|
||||
/// </summary>
|
||||
[EnumMember]
|
||||
AwaitingRelease
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum for the various statuses a Content object can have
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract]
|
||||
public enum ContentStatus
|
||||
{
|
||||
[EnumMember]
|
||||
Unpublished,
|
||||
[EnumMember]
|
||||
Published,
|
||||
[EnumMember]
|
||||
Expired,
|
||||
[EnumMember]
|
||||
Trashed,
|
||||
[EnumMember]
|
||||
AwaitingRelease
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="IContentBase"/> class, to manage tags.
|
||||
/// </summary>
|
||||
public static class ContentTagsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Assign tags.
|
||||
/// </summary>
|
||||
/// <param name="content">The content item.</param>
|
||||
/// <param name="propertyTypeAlias">The property alias.</param>
|
||||
/// <param name="tags">The tags.</param>
|
||||
/// <param name="merge">A value indicating whether to merge the tags with existing tags instead of replacing them.</param>
|
||||
/// <remarks>Tags do not support variants.</remarks>
|
||||
public static void AssignTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, bool merge = false)
|
||||
{
|
||||
content.GetTagProperty(propertyTypeAlias).AssignTags(tags, merge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove tags.
|
||||
/// </summary>
|
||||
/// <param name="content">The content item.</param>
|
||||
/// <param name="propertyTypeAlias">The property alias.</param>
|
||||
/// <param name="tags">The tags.</param>
|
||||
/// <remarks>Tags do not support variants.</remarks>
|
||||
public static void RemoveTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags)
|
||||
{
|
||||
content.GetTagProperty(propertyTypeAlias).RemoveTags(tags);
|
||||
}
|
||||
|
||||
// gets and validates the property
|
||||
private static Property GetTagProperty(this IContentBase content, string propertyTypeAlias)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
|
||||
var property = content.Properties[propertyTypeAlias];
|
||||
if (property != null) return property;
|
||||
|
||||
throw new IndexOutOfRangeException($"Could not find a property with alias \"{propertyTypeAlias}\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,164 +1,202 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the content type that a <see cref="Content"/> object is based on
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class ContentType : ContentTypeCompositionBase, IContentType
|
||||
{
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
public const bool IsPublishingConst = true;
|
||||
|
||||
private int _defaultTemplate;
|
||||
private IEnumerable<ITemplate> _allowedTemplates;
|
||||
|
||||
/// <summary>
|
||||
/// Constuctor for creating a ContentType with the parent's id.
|
||||
/// </summary>
|
||||
/// <remarks>Only use this for creating ContentTypes at the root (with ParentId -1).</remarks>
|
||||
/// <param name="parentId"></param>
|
||||
public ContentType(int parentId) : base(parentId)
|
||||
{
|
||||
_allowedTemplates = new List<ITemplate>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constuctor for creating a ContentType with the parent as an inherited type.
|
||||
/// </summary>
|
||||
/// <remarks>Use this to ensure inheritance from parent.</remarks>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="alias"></param>
|
||||
public ContentType(IContentType parent, string alias)
|
||||
: base(parent, alias)
|
||||
{
|
||||
_allowedTemplates = new List<ITemplate>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsPublishing => IsPublishingConst;
|
||||
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo DefaultTemplateSelector = ExpressionHelper.GetPropertyInfo<ContentType, int>(x => x.DefaultTemplateId);
|
||||
public readonly PropertyInfo AllowedTemplatesSelector = ExpressionHelper.GetPropertyInfo<ContentType, IEnumerable<ITemplate>>(x => x.AllowedTemplates);
|
||||
|
||||
//Custom comparer for enumerable
|
||||
public readonly DelegateEqualityComparer<IEnumerable<ITemplate>> TemplateComparer = new DelegateEqualityComparer<IEnumerable<ITemplate>>(
|
||||
(templates, enumerable) => templates.UnsortedSequenceEqual(enumerable),
|
||||
templates => templates.GetHashCode());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias of the default Template.
|
||||
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
|
||||
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
|
||||
/// we should not store direct entity
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public ITemplate DefaultTemplate
|
||||
{
|
||||
get { return AllowedTemplates.FirstOrDefault(x => x != null && x.Id == DefaultTemplateId); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal property to store the Id of the default template
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
internal int DefaultTemplateId
|
||||
{
|
||||
get { return _defaultTemplate; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _defaultTemplate, Ps.Value.DefaultTemplateSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a list of Templates which are allowed for the ContentType
|
||||
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
|
||||
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
|
||||
/// we should not store direct entity
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public IEnumerable<ITemplate> AllowedTemplates
|
||||
{
|
||||
get => _allowedTemplates;
|
||||
set
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the content type that a <see cref="Content"/> object is based on
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class ContentType : ContentTypeCompositionBase, IContentType
|
||||
{
|
||||
private int _defaultTemplate;
|
||||
private IEnumerable<ITemplate> _allowedTemplates;
|
||||
|
||||
/// <summary>
|
||||
/// Constuctor for creating a ContentType with the parent's id.
|
||||
/// </summary>
|
||||
/// <remarks>Only use this for creating ContentTypes at the root (with ParentId -1).</remarks>
|
||||
/// <param name="parentId"></param>
|
||||
public ContentType(int parentId) : base(parentId)
|
||||
{
|
||||
_allowedTemplates = new List<ITemplate>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constuctor for creating a ContentType with the parent as an inherited type.
|
||||
/// </summary>
|
||||
/// <remarks>Use this to ensure inheritance from parent.</remarks>
|
||||
/// <param name="parent"></param>
|
||||
[Obsolete("This method is obsolete, use ContentType(IContentType parent, string alias) instead.", false)]
|
||||
public ContentType(IContentType parent) : this(parent, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constuctor for creating a ContentType with the parent as an inherited type.
|
||||
/// </summary>
|
||||
/// <remarks>Use this to ensure inheritance from parent.</remarks>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="alias"></param>
|
||||
public ContentType(IContentType parent, string alias)
|
||||
: base(parent, alias)
|
||||
{
|
||||
_allowedTemplates = new List<ITemplate>();
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo DefaultTemplateSelector = ExpressionHelper.GetPropertyInfo<ContentType, int>(x => x.DefaultTemplateId);
|
||||
public readonly PropertyInfo AllowedTemplatesSelector = ExpressionHelper.GetPropertyInfo<ContentType, IEnumerable<ITemplate>>(x => x.AllowedTemplates);
|
||||
|
||||
//Custom comparer for enumerable
|
||||
public readonly DelegateEqualityComparer<IEnumerable<ITemplate>> TemplateComparer = new DelegateEqualityComparer<IEnumerable<ITemplate>>(
|
||||
(templates, enumerable) => templates.UnsortedSequenceEqual(enumerable),
|
||||
templates => templates.GetHashCode());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias of the default Template.
|
||||
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
|
||||
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
|
||||
/// we should not store direct entity
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public ITemplate DefaultTemplate
|
||||
{
|
||||
get { return AllowedTemplates.FirstOrDefault(x => x != null && x.Id == DefaultTemplateId); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal property to store the Id of the default template
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
internal int DefaultTemplateId
|
||||
{
|
||||
get { return _defaultTemplate; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _defaultTemplate, Ps.Value.DefaultTemplateSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a list of Templates which are allowed for the ContentType
|
||||
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
|
||||
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
|
||||
/// we should not store direct entity
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public IEnumerable<ITemplate> AllowedTemplates
|
||||
{
|
||||
get { return _allowedTemplates; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector, Ps.Value.TemplateComparer);
|
||||
|
||||
if (_allowedTemplates.Any(x => x.Id == _defaultTemplate) == false)
|
||||
DefaultTemplateId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if AllowedTemplates contains templateId
|
||||
/// </summary>
|
||||
/// <param name="templateId">The template id to check</param>
|
||||
/// <returns>True if AllowedTemplates contains the templateId else False</returns>
|
||||
public bool IsAllowedTemplate(int templateId)
|
||||
{
|
||||
return AllowedTemplates == null
|
||||
? false
|
||||
: AllowedTemplates.Any(t => t.Id == templateId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if AllowedTemplates contains templateId
|
||||
/// </summary>
|
||||
/// <param name="templateAlias">The template alias to check</param>
|
||||
/// <returns>True if AllowedTemplates contains the templateAlias else False</returns>
|
||||
public bool IsAllowedTemplate(string templateAlias)
|
||||
{
|
||||
return AllowedTemplates == null
|
||||
? false
|
||||
: AllowedTemplates.Any(t => t.Alias.Equals(templateAlias, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default template for the ContentType
|
||||
/// </summary>
|
||||
/// <param name="template">Default <see cref="ITemplate"/></param>
|
||||
public void SetDefaultTemplate(ITemplate template)
|
||||
{
|
||||
if (template == null)
|
||||
{
|
||||
DefaultTemplateId = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
DefaultTemplateId = template.Id;
|
||||
if (_allowedTemplates.Any(x => x != null && x.Id == template.Id) == false)
|
||||
{
|
||||
var templates = AllowedTemplates.ToList();
|
||||
templates.Add(template);
|
||||
AllowedTemplates = templates;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a template from the list of allowed templates
|
||||
/// </summary>
|
||||
/// <param name="template"><see cref="ITemplate"/> to remove</param>
|
||||
/// <returns>True if template was removed, otherwise False</returns>
|
||||
public bool RemoveTemplate(ITemplate template)
|
||||
{
|
||||
if (DefaultTemplateId == template.Id)
|
||||
DefaultTemplateId = default(int);
|
||||
|
||||
var templates = AllowedTemplates.ToList();
|
||||
var remove = templates.FirstOrDefault(x => x.Id == template.Id);
|
||||
var result = templates.Remove(remove);
|
||||
AllowedTemplates = templates;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_allowedTemplates.Any(x => x.Id == _defaultTemplate) == false)
|
||||
DefaultTemplateId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if AllowedTemplates contains templateId
|
||||
/// </summary>
|
||||
/// <param name="templateId">The template id to check</param>
|
||||
/// <returns>True if AllowedTemplates contains the templateId else False</returns>
|
||||
public bool IsAllowedTemplate(int templateId)
|
||||
{
|
||||
var allowedTemplates = AllowedTemplates ?? new ITemplate[0];
|
||||
return allowedTemplates.Any(t => t.Id == templateId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if AllowedTemplates contains templateId
|
||||
/// </summary>
|
||||
/// <param name="templateAlias">The template alias to check</param>
|
||||
/// <returns>True if AllowedTemplates contains the templateAlias else False</returns>
|
||||
public bool IsAllowedTemplate(string templateAlias)
|
||||
{
|
||||
var allowedTemplates = AllowedTemplates ?? new ITemplate[0];
|
||||
return allowedTemplates.Any(t => t.Alias.Equals(templateAlias, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default template for the ContentType
|
||||
/// </summary>
|
||||
/// <param name="template">Default <see cref="ITemplate"/></param>
|
||||
public void SetDefaultTemplate(ITemplate template)
|
||||
{
|
||||
if (template == null)
|
||||
{
|
||||
DefaultTemplateId = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
DefaultTemplateId = template.Id;
|
||||
if (_allowedTemplates.Any(x => x != null && x.Id == template.Id) == false)
|
||||
{
|
||||
var templates = AllowedTemplates.ToList();
|
||||
templates.Add(template);
|
||||
AllowedTemplates = templates;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a template from the list of allowed templates
|
||||
/// </summary>
|
||||
/// <param name="template"><see cref="ITemplate"/> to remove</param>
|
||||
/// <returns>True if template was removed, otherwise False</returns>
|
||||
public bool RemoveTemplate(ITemplate template)
|
||||
{
|
||||
if (DefaultTemplateId == template.Id)
|
||||
DefaultTemplateId = default(int);
|
||||
|
||||
var templates = AllowedTemplates.ToList();
|
||||
var remove = templates.FirstOrDefault(x => x.Id == template.Id);
|
||||
var result = templates.Remove(remove);
|
||||
AllowedTemplates = templates;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use DeepCloneWithResetIdentities instead")]
|
||||
public IContentType Clone(string alias)
|
||||
{
|
||||
return DeepCloneWithResetIdentities(alias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContentType DeepCloneWithResetIdentities(string alias)
|
||||
{
|
||||
var clone = (ContentType)DeepClone();
|
||||
clone.Alias = alias;
|
||||
clone.Key = Guid.Empty;
|
||||
foreach (var propertyGroup in clone.PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetIdentity();
|
||||
propertyGroup.ResetDirtyProperties(false);
|
||||
}
|
||||
foreach (var propertyType in clone.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Core.Models
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used when determining available compositions for a given content type
|
||||
@@ -14,4 +14,4 @@
|
||||
public IContentTypeComposition Composition { get; private set; }
|
||||
public bool Allowed { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
@@ -23,4 +23,4 @@ namespace Umbraco.Core.Models
|
||||
public IEnumerable<IContentTypeComposition> Ancestors { get; private set; }
|
||||
public IEnumerable<ContentTypeAvailableCompositionsResult> Results { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,67 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extensions methods for <see cref="IContentTypeBase"/>.
|
||||
/// </summary>
|
||||
public static class ContentTypeBaseExtensions
|
||||
{
|
||||
public static PublishedItemType GetItemType(this IContentTypeBase contentType)
|
||||
{
|
||||
var type = contentType.GetType();
|
||||
var itemType = PublishedItemType.Unknown;
|
||||
if (typeof(IContentType).IsAssignableFrom(type)) itemType = PublishedItemType.Content;
|
||||
else if (typeof(IMediaType).IsAssignableFrom(type)) itemType = PublishedItemType.Media;
|
||||
else if (typeof(IMemberType).IsAssignableFrom(type)) itemType = PublishedItemType.Member;
|
||||
return itemType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to check if any property type was changed between variant/invariant
|
||||
/// </summary>
|
||||
/// <param name="contentType"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool WasPropertyTypeVariationChanged(this IContentTypeBase contentType)
|
||||
{
|
||||
return contentType.WasPropertyTypeVariationChanged(out var _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to check if any property type was changed between variant/invariant
|
||||
/// </summary>
|
||||
/// <param name="contentType"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool WasPropertyTypeVariationChanged(this IContentTypeBase contentType, out IReadOnlyCollection<string> aliases)
|
||||
{
|
||||
var a = new List<string>();
|
||||
|
||||
// property variation change?
|
||||
var hasAnyPropertyVariationChanged = contentType.PropertyTypes.Any(propertyType =>
|
||||
{
|
||||
if (!(propertyType is IRememberBeingDirty dirtyProperty))
|
||||
throw new Exception("oops");
|
||||
|
||||
// skip new properties
|
||||
//TODO: This used to be WasPropertyDirty("HasIdentity") but i don't think that actually worked for detecting new entities this does seem to work properly
|
||||
var isNewProperty = dirtyProperty.WasPropertyDirty("Id");
|
||||
if (isNewProperty) return false;
|
||||
|
||||
// variation change?
|
||||
var dirty = dirtyProperty.WasPropertyDirty("Variations");
|
||||
if (dirty)
|
||||
a.Add(propertyType.Alias);
|
||||
|
||||
return dirty;
|
||||
|
||||
});
|
||||
|
||||
aliases = a;
|
||||
return hasAnyPropertyVariationChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,325 +1,275 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an abstract class for composition specific ContentType properties and methods
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentTypeComposition
|
||||
{
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private List<IContentTypeComposition> _contentTypeComposition = new List<IContentTypeComposition>();
|
||||
internal List<int> RemovedContentTypeKeyTracker = new List<int>();
|
||||
|
||||
protected ContentTypeCompositionBase(int parentId) : base(parentId)
|
||||
{ }
|
||||
|
||||
protected ContentTypeCompositionBase(IContentTypeComposition parent)
|
||||
: this(parent, null)
|
||||
{ }
|
||||
|
||||
protected ContentTypeCompositionBase(IContentTypeComposition parent, string alias)
|
||||
: base(parent, alias)
|
||||
{
|
||||
AddContentType(parent);
|
||||
}
|
||||
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo ContentTypeCompositionSelector =
|
||||
ExpressionHelper.GetPropertyInfo<ContentTypeCompositionBase, IEnumerable<IContentTypeComposition>>(x => x.ContentTypeComposition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content types that compose this content type.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public IEnumerable<IContentTypeComposition> ContentTypeComposition
|
||||
{
|
||||
get => _contentTypeComposition;
|
||||
set
|
||||
{
|
||||
_contentTypeComposition = value.ToList();
|
||||
OnPropertyChanged(Ps.Value.ContentTypeCompositionSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property groups for the entire composition.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public IEnumerable<PropertyGroup> CompositionPropertyGroups
|
||||
{
|
||||
get
|
||||
{
|
||||
// we need to "acquire" composition groups and properties here, ie get our own clones,
|
||||
// so that we can change their variation according to this content type variations.
|
||||
//
|
||||
// it would be nice to cache the resulting enumerable, but alas we cannot, otherwise
|
||||
// any change to compositions are ignored and that breaks many things - and tracking
|
||||
// changes to refresh the cache would be expensive.
|
||||
|
||||
void AcquireProperty(PropertyType propertyType)
|
||||
{
|
||||
propertyType.Variations = propertyType.Variations & Variations;
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
return ContentTypeComposition.SelectMany(x => x.CompositionPropertyGroups)
|
||||
.Select(group =>
|
||||
{
|
||||
group = (PropertyGroup) group.DeepClone();
|
||||
foreach (var property in group.PropertyTypes)
|
||||
AcquireProperty(property);
|
||||
return group;
|
||||
})
|
||||
.Union(PropertyGroups);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property types for the entire composition.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public IEnumerable<PropertyType> CompositionPropertyTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
// we need to "acquire" composition properties here, ie get our own clones,
|
||||
// so that we can change their variation according to this content type variations.
|
||||
//
|
||||
// see note in CompositionPropertyGroups for comments on caching the resulting enumerable
|
||||
|
||||
PropertyType AcquireProperty(PropertyType propertyType)
|
||||
{
|
||||
propertyType = (PropertyType) propertyType.DeepClone();
|
||||
propertyType.Variations = propertyType.Variations & Variations;
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
return ContentTypeComposition
|
||||
.SelectMany(x => x.CompositionPropertyTypes)
|
||||
.Select(AcquireProperty)
|
||||
.Union(PropertyTypes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property types obtained via composition.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Gets them raw, ie with their original variation.</para>
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
internal IEnumerable<PropertyType> RawComposedPropertyTypes => GetRawComposedPropertyTypes();
|
||||
|
||||
private IEnumerable<PropertyType> GetRawComposedPropertyTypes(bool start = true)
|
||||
{
|
||||
var propertyTypes = ContentTypeComposition
|
||||
.Cast<ContentTypeCompositionBase>()
|
||||
.SelectMany(x => start ? x.GetRawComposedPropertyTypes(false) : x.CompositionPropertyTypes);
|
||||
|
||||
if (!start)
|
||||
propertyTypes = propertyTypes.Union(PropertyTypes);
|
||||
|
||||
return propertyTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a content type to the composition.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type to add.</param>
|
||||
/// <returns>True if the content type was added, otherwise false.</returns>
|
||||
public bool AddContentType(IContentTypeComposition contentType)
|
||||
{
|
||||
if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists)))
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrEmpty(Alias) == false && Alias.Equals(contentType.Alias))
|
||||
return false;
|
||||
|
||||
if (ContentTypeCompositionExists(contentType.Alias) == false)
|
||||
{
|
||||
//Before we actually go ahead and add the ContentType as a Composition we ensure that we don't
|
||||
//end up with duplicate PropertyType aliases - in which case we throw an exception.
|
||||
var conflictingPropertyTypeAliases = CompositionPropertyTypes.SelectMany(
|
||||
x => contentType.CompositionPropertyTypes
|
||||
.Where(y => y.Alias.Equals(x.Alias, StringComparison.InvariantCultureIgnoreCase))
|
||||
.Select(p => p.Alias)).ToList();
|
||||
|
||||
if (conflictingPropertyTypeAliases.Any())
|
||||
throw new InvalidCompositionException(Alias, contentType.Alias, conflictingPropertyTypeAliases.ToArray());
|
||||
|
||||
_contentTypeComposition.Add(contentType);
|
||||
OnPropertyChanged(Ps.Value.ContentTypeCompositionSelector);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a content type with a specified alias from the composition.
|
||||
/// </summary>
|
||||
/// <param name="alias">The alias of the content type to remove.</param>
|
||||
/// <returns>True if the content type was removed, otherwise false.</returns>
|
||||
public bool RemoveContentType(string alias)
|
||||
{
|
||||
if (ContentTypeCompositionExists(alias))
|
||||
{
|
||||
var contentTypeComposition = ContentTypeComposition.FirstOrDefault(x => x.Alias == alias);
|
||||
if (contentTypeComposition == null)//You can't remove a composition from another composition
|
||||
return false;
|
||||
|
||||
RemovedContentTypeKeyTracker.Add(contentTypeComposition.Id);
|
||||
|
||||
//If the ContentType we are removing has Compositions of its own these needs to be removed as well
|
||||
var compositionIdsToRemove = contentTypeComposition.CompositionIds().ToList();
|
||||
if (compositionIdsToRemove.Any())
|
||||
RemovedContentTypeKeyTracker.AddRange(compositionIdsToRemove);
|
||||
|
||||
OnPropertyChanged(Ps.Value.ContentTypeCompositionSelector);
|
||||
return _contentTypeComposition.Remove(contentTypeComposition);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a ContentType with the supplied alias exists in the list of composite ContentTypes
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of a <see cref="ContentType"/></param>
|
||||
/// <returns>True if ContentType with alias exists, otherwise returns False</returns>
|
||||
public bool ContentTypeCompositionExists(string alias)
|
||||
{
|
||||
if (ContentTypeComposition.Any(x => x.Alias.Equals(alias)))
|
||||
return true;
|
||||
|
||||
if (ContentTypeComposition.Any(x => x.ContentTypeCompositionExists(alias)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a PropertyType with a given alias already exists
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyType with the passed in alias exists, otherwise <c>False</c></returns>
|
||||
public override bool PropertyTypeExists(string propertyTypeAlias)
|
||||
{
|
||||
return CompositionPropertyTypes.Any(x => x.Alias == propertyTypeAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyGroup.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the PropertyGroup to add</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyGroup with the passed in name was added, otherwise <c>False</c></returns>
|
||||
public override bool AddPropertyGroup(string groupName)
|
||||
{
|
||||
return AddAndReturnPropertyGroup(groupName) != null;
|
||||
}
|
||||
|
||||
private PropertyGroup AddAndReturnPropertyGroup(string name)
|
||||
{
|
||||
// ensure we don't have it already
|
||||
if (PropertyGroups.Any(x => x.Name == name))
|
||||
return null;
|
||||
|
||||
// create the new group
|
||||
var group = new PropertyGroup(IsPublishing) { Name = name, SortOrder = 0 };
|
||||
|
||||
// check if it is inherited - there might be more than 1 but we want the 1st, to
|
||||
// reuse its sort order - if there are more than 1 and they have different sort
|
||||
// orders... there isn't much we can do anyways
|
||||
var inheritGroup = CompositionPropertyGroups.FirstOrDefault(x => x.Name == name);
|
||||
if (inheritGroup == null)
|
||||
{
|
||||
// no, just local, set sort order
|
||||
var lastGroup = PropertyGroups.LastOrDefault();
|
||||
if (lastGroup != null)
|
||||
group.SortOrder = lastGroup.SortOrder + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// yes, inherited, re-use sort order
|
||||
group.SortOrder = inheritGroup.SortOrder;
|
||||
}
|
||||
|
||||
// add
|
||||
PropertyGroups.Add(group);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType to a specific PropertyGroup
|
||||
/// </summary>
|
||||
/// <param name="propertyType"><see cref="PropertyType"/> to add</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to add the PropertyType to</param>
|
||||
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
|
||||
public override bool AddPropertyType(PropertyType propertyType, string propertyGroupName)
|
||||
{
|
||||
// ensure no duplicate alias - over all composition properties
|
||||
if (PropertyTypeExists(propertyType.Alias))
|
||||
return false;
|
||||
|
||||
// get and ensure a group local to this content type
|
||||
var group = PropertyGroups.Contains(propertyGroupName)
|
||||
? PropertyGroups[propertyGroupName]
|
||||
: AddAndReturnPropertyGroup(propertyGroupName);
|
||||
if (group == null)
|
||||
return false;
|
||||
|
||||
// add property to group
|
||||
propertyType.PropertyGroupId = new Lazy<int>(() => group.Id);
|
||||
group.PropertyTypes.Add(propertyType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of ContentType aliases from the current composition
|
||||
/// </summary>
|
||||
/// <returns>An enumerable list of string aliases</returns>
|
||||
/// <remarks>Does not contain the alias of the Current ContentType</remarks>
|
||||
public IEnumerable<string> CompositionAliases()
|
||||
{
|
||||
return ContentTypeComposition
|
||||
.Select(x => x.Alias)
|
||||
.Union(ContentTypeComposition.SelectMany(x => x.CompositionAliases()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of ContentType Ids from the current composition
|
||||
/// </summary>
|
||||
/// <returns>An enumerable list of integer ids</returns>
|
||||
/// <remarks>Does not contain the Id of the Current ContentType</remarks>
|
||||
public IEnumerable<int> CompositionIds()
|
||||
{
|
||||
return ContentTypeComposition
|
||||
.Select(x => x.Id)
|
||||
.Union(ContentTypeComposition.SelectMany(x => x.CompositionIds()));
|
||||
}
|
||||
|
||||
protected override void PerformDeepClone(object clone)
|
||||
{
|
||||
base.PerformDeepClone(clone);
|
||||
|
||||
var clonedEntity = (ContentTypeCompositionBase)clone;
|
||||
|
||||
//need to manually assign since this is an internal field and will not be automatically mapped
|
||||
clonedEntity.RemovedContentTypeKeyTracker = new List<int>();
|
||||
clonedEntity._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an abstract class for composition specific ContentType properties and methods
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentTypeComposition
|
||||
{
|
||||
private List<IContentTypeComposition> _contentTypeComposition = new List<IContentTypeComposition>();
|
||||
internal List<int> RemovedContentTypeKeyTracker = new List<int>();
|
||||
|
||||
protected ContentTypeCompositionBase(int parentId) : base(parentId)
|
||||
{
|
||||
}
|
||||
|
||||
protected ContentTypeCompositionBase(IContentTypeComposition parent)
|
||||
: this(parent, null)
|
||||
{
|
||||
}
|
||||
|
||||
protected ContentTypeCompositionBase(IContentTypeComposition parent, string alias)
|
||||
: base(parent, alias)
|
||||
{
|
||||
AddContentType(parent);
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo ContentTypeCompositionSelector =
|
||||
ExpressionHelper.GetPropertyInfo<ContentTypeCompositionBase, IEnumerable<IContentTypeComposition>>(x => x.ContentTypeComposition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content types that compose this content type.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public IEnumerable<IContentTypeComposition> ContentTypeComposition
|
||||
{
|
||||
get { return _contentTypeComposition; }
|
||||
set
|
||||
{
|
||||
_contentTypeComposition = value.ToList();
|
||||
OnPropertyChanged(Ps.Value.ContentTypeCompositionSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property groups for the entire composition.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public IEnumerable<PropertyGroup> CompositionPropertyGroups
|
||||
{
|
||||
get
|
||||
{
|
||||
var groups = ContentTypeComposition.SelectMany(x => x.CompositionPropertyGroups).Union(PropertyGroups);
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property types for the entire composition.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public IEnumerable<PropertyType> CompositionPropertyTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
var propertyTypes = ContentTypeComposition.SelectMany(x => x.CompositionPropertyTypes).Union(PropertyTypes);
|
||||
return propertyTypes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a content type to the composition.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type to add.</param>
|
||||
/// <returns>True if the content type was added, otherwise false.</returns>
|
||||
public bool AddContentType(IContentTypeComposition contentType)
|
||||
{
|
||||
if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists)))
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrEmpty(Alias) == false && Alias.Equals(contentType.Alias))
|
||||
return false;
|
||||
|
||||
if (ContentTypeCompositionExists(contentType.Alias) == false)
|
||||
{
|
||||
//Before we actually go ahead and add the ContentType as a Composition we ensure that we don't
|
||||
//end up with duplicate PropertyType aliases - in which case we throw an exception.
|
||||
var conflictingPropertyTypeAliases = CompositionPropertyTypes.SelectMany(
|
||||
x => contentType.CompositionPropertyTypes
|
||||
.Where(y => y.Alias.Equals(x.Alias, StringComparison.InvariantCultureIgnoreCase))
|
||||
.Select(p => p.Alias)).ToList();
|
||||
|
||||
if (conflictingPropertyTypeAliases.Any())
|
||||
throw new InvalidCompositionException(Alias, contentType.Alias, conflictingPropertyTypeAliases.ToArray());
|
||||
|
||||
_contentTypeComposition.Add(contentType);
|
||||
OnPropertyChanged(Ps.Value.ContentTypeCompositionSelector);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a content type with a specified alias from the composition.
|
||||
/// </summary>
|
||||
/// <param name="alias">The alias of the content type to remove.</param>
|
||||
/// <returns>True if the content type was removed, otherwise false.</returns>
|
||||
public bool RemoveContentType(string alias)
|
||||
{
|
||||
if (ContentTypeCompositionExists(alias))
|
||||
{
|
||||
var contentTypeComposition = ContentTypeComposition.FirstOrDefault(x => x.Alias == alias);
|
||||
if (contentTypeComposition == null)//You can't remove a composition from another composition
|
||||
return false;
|
||||
|
||||
RemovedContentTypeKeyTracker.Add(contentTypeComposition.Id);
|
||||
|
||||
//If the ContentType we are removing has Compositions of its own these needs to be removed as well
|
||||
var compositionIdsToRemove = contentTypeComposition.CompositionIds().ToList();
|
||||
if (compositionIdsToRemove.Any())
|
||||
RemovedContentTypeKeyTracker.AddRange(compositionIdsToRemove);
|
||||
|
||||
OnPropertyChanged(Ps.Value.ContentTypeCompositionSelector);
|
||||
return _contentTypeComposition.Remove(contentTypeComposition);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a ContentType with the supplied alias exists in the list of composite ContentTypes
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of a <see cref="ContentType"/></param>
|
||||
/// <returns>True if ContentType with alias exists, otherwise returns False</returns>
|
||||
public bool ContentTypeCompositionExists(string alias)
|
||||
{
|
||||
if (ContentTypeComposition.Any(x => x.Alias.Equals(alias)))
|
||||
return true;
|
||||
|
||||
if (ContentTypeComposition.Any(x => x.ContentTypeCompositionExists(alias)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a PropertyType with a given alias already exists
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyType with the passed in alias exists, otherwise <c>False</c></returns>
|
||||
public override bool PropertyTypeExists(string propertyTypeAlias)
|
||||
{
|
||||
return CompositionPropertyTypes.Any(x => x.Alias == propertyTypeAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyGroup.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the PropertyGroup to add</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyGroup with the passed in name was added, otherwise <c>False</c></returns>
|
||||
public override bool AddPropertyGroup(string groupName)
|
||||
{
|
||||
return AddAndReturnPropertyGroup(groupName) != null;
|
||||
}
|
||||
|
||||
private PropertyGroup AddAndReturnPropertyGroup(string name)
|
||||
{
|
||||
// ensure we don't have it already
|
||||
if (PropertyGroups.Any(x => x.Name == name))
|
||||
return null;
|
||||
|
||||
// create the new group
|
||||
var group = new PropertyGroup { Name = name, SortOrder = 0 };
|
||||
|
||||
// check if it is inherited - there might be more than 1 but we want the 1st, to
|
||||
// reuse its sort order - if there are more than 1 and they have different sort
|
||||
// orders... there isn't much we can do anyways
|
||||
var inheritGroup = CompositionPropertyGroups.FirstOrDefault(x => x.Name == name);
|
||||
if (inheritGroup == null)
|
||||
{
|
||||
// no, just local, set sort order
|
||||
var lastGroup = PropertyGroups.LastOrDefault();
|
||||
if (lastGroup != null)
|
||||
group.SortOrder = lastGroup.SortOrder + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// yes, inherited, re-use sort order
|
||||
group.SortOrder = inheritGroup.SortOrder;
|
||||
}
|
||||
|
||||
// add
|
||||
PropertyGroups.Add(group);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType to a specific PropertyGroup
|
||||
/// </summary>
|
||||
/// <param name="propertyType"><see cref="PropertyType"/> to add</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to add the PropertyType to</param>
|
||||
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
|
||||
public override bool AddPropertyType(PropertyType propertyType, string propertyGroupName)
|
||||
{
|
||||
// ensure no duplicate alias - over all composition properties
|
||||
if (PropertyTypeExists(propertyType.Alias))
|
||||
return false;
|
||||
|
||||
// get and ensure a group local to this content type
|
||||
var group = PropertyGroups.Contains(propertyGroupName)
|
||||
? PropertyGroups[propertyGroupName]
|
||||
: AddAndReturnPropertyGroup(propertyGroupName);
|
||||
if (group == null)
|
||||
return false;
|
||||
|
||||
// add property to group
|
||||
propertyType.PropertyGroupId = new Lazy<int>(() => group.Id);
|
||||
group.PropertyTypes.Add(propertyType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of ContentType aliases from the current composition
|
||||
/// </summary>
|
||||
/// <returns>An enumerable list of string aliases</returns>
|
||||
/// <remarks>Does not contain the alias of the Current ContentType</remarks>
|
||||
public IEnumerable<string> CompositionAliases()
|
||||
{
|
||||
return ContentTypeComposition
|
||||
.Select(x => x.Alias)
|
||||
.Union(ContentTypeComposition.SelectMany(x => x.CompositionAliases()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of ContentType Ids from the current composition
|
||||
/// </summary>
|
||||
/// <returns>An enumerable list of integer ids</returns>
|
||||
/// <remarks>Does not contain the Id of the Current ContentType</remarks>
|
||||
public IEnumerable<int> CompositionIds()
|
||||
{
|
||||
return ContentTypeComposition
|
||||
.Select(x => x.Id)
|
||||
.Union(ContentTypeComposition.SelectMany(x => x.CompositionIds()));
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeCompositionBase)base.DeepClone();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually assign since this is an internal field and will not be automatically mapped
|
||||
clone.RemovedContentTypeKeyTracker = new List<int>();
|
||||
clone._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +1,80 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a POCO for setting sort order on a ContentType reference
|
||||
/// </summary>
|
||||
public class ContentTypeSort : IValueObject, IDeepCloneable
|
||||
{
|
||||
// this parameterless ctor should never be used BUT is required by AutoMapper in EntityMapperProfile
|
||||
internal ContentTypeSort() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
|
||||
/// </summary>
|
||||
public ContentTypeSort(int id, int sortOrder)
|
||||
{
|
||||
Id = new Lazy<int>(() => id);
|
||||
SortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public ContentTypeSort(Lazy<int> id, int sortOrder, string @alias)
|
||||
{
|
||||
Id = id;
|
||||
SortOrder = sortOrder;
|
||||
Alias = alias;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Id of the ContentType
|
||||
/// </summary>
|
||||
public Lazy<int> Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Sort Order of the ContentType
|
||||
/// </summary>
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Alias of the ContentType
|
||||
/// </summary>
|
||||
public string Alias { get; set; }
|
||||
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeSort)MemberwiseClone();
|
||||
var id = Id.Value;
|
||||
clone.Id = new Lazy<int>(() => id);
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected bool Equals(ContentTypeSort other)
|
||||
{
|
||||
return Id.Value.Equals(other.Id.Value) && string.Equals(Alias, other.Alias);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((ContentTypeSort) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
//The hash code will just be the alias if one is assigned, otherwise it will be the hash code of the Id.
|
||||
//In some cases the alias can be null of the non lazy ctor is used, in that case, the lazy Id will already have a value created.
|
||||
return Alias != null ? Alias.GetHashCode() : (Id.Value.GetHashCode() * 397);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a POCO for setting sort order on a ContentType reference
|
||||
/// </summary>
|
||||
public class ContentTypeSort : IValueObject, IDeepCloneable
|
||||
{
|
||||
[Obsolete("This parameterless constructor should never be used")]
|
||||
public ContentTypeSort()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
|
||||
/// </summary>
|
||||
public ContentTypeSort(int id, int sortOrder)
|
||||
{
|
||||
Id = new Lazy<int>(() => id);
|
||||
SortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public ContentTypeSort(Lazy<int> id, int sortOrder, string @alias)
|
||||
{
|
||||
Id = id;
|
||||
SortOrder = sortOrder;
|
||||
Alias = alias;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Id of the ContentType
|
||||
/// </summary>
|
||||
public Lazy<int> Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Sort Order of the ContentType
|
||||
/// </summary>
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Alias of the ContentType
|
||||
/// </summary>
|
||||
public string Alias { get; set; }
|
||||
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeSort)MemberwiseClone();
|
||||
var id = Id.Value;
|
||||
clone.Id = new Lazy<int>(() => id);
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected bool Equals(ContentTypeSort other)
|
||||
{
|
||||
return Id.Value.Equals(other.Id.Value) && string.Equals(Alias, other.Alias);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((ContentTypeSort) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
//The hash code will just be the alias if one is assigned, otherwise it will be the hash code of the Id.
|
||||
//In some cases the alias can be null of the non lazy ctor is used, in that case, the lazy Id will already have a value created.
|
||||
return Alias != null ? Alias.GetHashCode() : (Id.Value.GetHashCode() * 397);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates how values can vary.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Values can vary by nothing, or culture, or segment, or both.</para>
|
||||
/// <para>Varying by culture implies that each culture version of a document can
|
||||
/// be available or not, and published or not, individually. Varying by segment
|
||||
/// is a property-level thing.</para>
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
public enum ContentVariation : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Values do not vary.
|
||||
/// </summary>
|
||||
Nothing = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Values vary by culture.
|
||||
/// </summary>
|
||||
Culture = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Values vary by segment.
|
||||
/// </summary>
|
||||
Segment = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Values vary by culture and segment.
|
||||
/// </summary>
|
||||
CultureAndSegment = Culture | Segment
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used in content/media/member repositories in order to add this type of entity to the persisted collection to be saved
|
||||
/// in a single transaction during saving an entity
|
||||
/// </summary>
|
||||
internal class ContentXmlEntity<TContent> : IAggregateRoot
|
||||
where TContent : IContentBase
|
||||
{
|
||||
private readonly Func<TContent, XElement> _xml;
|
||||
|
||||
public ContentXmlEntity(TContent content, Func<TContent, XElement> xml)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
_xml = xml;
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public ContentXmlEntity(TContent content)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public XElement Xml
|
||||
{
|
||||
get { return _xml(Content); }
|
||||
}
|
||||
|
||||
public TContent Content { get; private set; }
|
||||
|
||||
public int Id
|
||||
{
|
||||
get { return Content.Id; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public Guid Key { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Special case, always return false, this will cause the repositories managing
|
||||
/// this object to always do an 'insert' but these are special repositories that
|
||||
/// do an InsertOrUpdate on insert since the data for this needs to be managed this way
|
||||
/// </summary>
|
||||
public bool HasIdentity
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (ContentXmlEntity<TContent>)MemberwiseClone();
|
||||
//Automatically deep clone ref properties that are IDeepCloneable
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IDataType"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class DataType : TreeEntityBase, IDataType
|
||||
{
|
||||
private static PropertySelectors _selectors;
|
||||
|
||||
private IDataEditor _editor;
|
||||
private ValueStorageType _databaseType;
|
||||
private object _configuration;
|
||||
private bool _hasConfiguration;
|
||||
private string _configurationJson;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataType"/> class.
|
||||
/// </summary>
|
||||
public DataType(IDataEditor editor, int parentId = -1)
|
||||
{
|
||||
_editor = editor ?? throw new ArgumentNullException(nameof(editor));
|
||||
ParentId = parentId;
|
||||
|
||||
// set a default configuration
|
||||
Configuration = _editor.GetConfigurationEditor().DefaultConfigurationObject;
|
||||
}
|
||||
|
||||
private static PropertySelectors Selectors => _selectors ?? (_selectors = new PropertySelectors());
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo Editor = ExpressionHelper.GetPropertyInfo<DataType, IDataEditor>(x => x.Editor);
|
||||
public readonly PropertyInfo DatabaseType = ExpressionHelper.GetPropertyInfo<DataType, ValueStorageType>(x => x.DatabaseType);
|
||||
public readonly PropertyInfo Configuration = ExpressionHelper.GetPropertyInfo<DataType, object>(x => x.Configuration);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
public IDataEditor Editor
|
||||
{
|
||||
get => _editor;
|
||||
set
|
||||
{
|
||||
// ignore if no change
|
||||
if (_editor.Alias == value.Alias) return;
|
||||
OnPropertyChanged(Selectors.Editor);
|
||||
|
||||
// try to map the existing configuration to the new configuration
|
||||
// simulate saving to db and reloading (ie go via json)
|
||||
var configuration = Configuration;
|
||||
var json = JsonConvert.SerializeObject(configuration);
|
||||
_editor = value;
|
||||
Configuration = _editor.GetConfigurationEditor().FromDatabase(json);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public string EditorAlias => _editor.Alias;
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public ValueStorageType DatabaseType
|
||||
{
|
||||
get => _databaseType;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _databaseType, Selectors.DatabaseType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public object Configuration
|
||||
{
|
||||
get
|
||||
{
|
||||
// if we know we have a configuration (which may be null), return it
|
||||
// if we don't have an editor, then we have no configuration, return null
|
||||
// else, use the editor to get the configuration object
|
||||
|
||||
if (_hasConfiguration) return _configuration;
|
||||
|
||||
_configuration = _editor.GetConfigurationEditor().FromDatabase(_configurationJson);
|
||||
_hasConfiguration = true;
|
||||
_configurationJson = null;
|
||||
|
||||
return _configuration;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
// we don't support re-assigning the same object
|
||||
// configurations are kinda non-mutable, mainly because detecting changes would be a pain
|
||||
if (_configuration == value) // reference comparison
|
||||
throw new ArgumentException("Configurations are kinda non-mutable. Do not reassign the same object.", nameof(value));
|
||||
|
||||
// validate configuration type
|
||||
if (!_editor.GetConfigurationEditor().IsConfiguration(value))
|
||||
throw new ArgumentException($"Value of type {value.GetType().Name} cannot be a configuration for editor {_editor.Alias}, expecting.", nameof(value));
|
||||
|
||||
// extract database type from configuration object, if appropriate
|
||||
if (value is IConfigureValueType valueTypeConfiguration)
|
||||
DatabaseType = ValueTypes.ToStorageType(valueTypeConfiguration.ValueType);
|
||||
|
||||
// extract database type from dictionary, if appropriate
|
||||
if (value is IDictionary<string, object> dictionaryConfiguration
|
||||
&& dictionaryConfiguration.TryGetValue(Constants.PropertyEditors.ConfigurationKeys.DataValueType, out var valueTypeObject)
|
||||
&& valueTypeObject is string valueTypeString
|
||||
&& ValueTypes.IsValue(valueTypeString))
|
||||
DatabaseType = ValueTypes.ToStorageType(valueTypeString);
|
||||
|
||||
_configuration = value;
|
||||
_hasConfiguration = true;
|
||||
_configurationJson = null;
|
||||
|
||||
// it's always a change
|
||||
OnPropertyChanged(Selectors.Configuration);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lazily set the configuration as a serialized json string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Will be de-serialized on-demand.</para>
|
||||
/// <para>This method is meant to be used when building entities from database, exclusively.
|
||||
/// It does NOT register a property change to dirty. It ignores the fact that the configuration
|
||||
/// may contain the database type, because the datatype DTO should also contain that database
|
||||
/// type, and they should be the same.</para>
|
||||
/// <para>Think before using!</para>
|
||||
/// </remarks>
|
||||
internal void SetLazyConfiguration(string configurationJson)
|
||||
{
|
||||
_hasConfiguration = false;
|
||||
_configuration = null;
|
||||
_configurationJson = configurationJson;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a lazy configuration.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The configuration object will be lazily de-serialized.</para>
|
||||
/// <para>This method is meant to be used when creating published datatypes, exclusively.</para>
|
||||
/// <para>Think before using!</para>
|
||||
/// </remarks>
|
||||
internal Lazy<object> GetLazyConfiguration()
|
||||
{
|
||||
// note: in both cases, make sure we capture what we need - we don't want
|
||||
// to capture a reference to this full, potentially heavy, DataType instance.
|
||||
|
||||
if (_hasConfiguration)
|
||||
{
|
||||
// if configuration has already been de-serialized, return
|
||||
var capturedConfiguration = _configuration;
|
||||
return new Lazy<object>(() => capturedConfiguration);
|
||||
}
|
||||
else
|
||||
{
|
||||
// else, create a Lazy de-serializer
|
||||
var capturedConfiguration = _configurationJson;
|
||||
var capturedEditor = _editor;
|
||||
return new Lazy<object>(() => capturedEditor.GetConfigurationEditor().FromDatabase(capturedConfiguration));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum of the various DbTypes for which the Property values are stored
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract]
|
||||
public enum DataTypeDatabaseType
|
||||
{
|
||||
[EnumMember]
|
||||
Ntext,
|
||||
[EnumMember]
|
||||
Nvarchar,
|
||||
[EnumMember]
|
||||
Integer,
|
||||
[EnumMember]
|
||||
Date,
|
||||
[EnumMember]
|
||||
Decimal
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Definition of a DataType/PropertyEditor
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The definition exists as a database reference between an actual DataType/PropertyEditor
|
||||
/// (identified by its control id), its prevalues (configuration) and the named DataType in the backoffice UI.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class DataTypeDefinition : Entity, IDataTypeDefinition
|
||||
{
|
||||
private int _parentId;
|
||||
private string _name;
|
||||
private int _sortOrder;
|
||||
private int _level;
|
||||
private string _path;
|
||||
private int _creatorId;
|
||||
private bool _trashed;
|
||||
private string _propertyEditorAlias;
|
||||
private DataTypeDatabaseType _databaseType;
|
||||
|
||||
[Obsolete("Property editor's are defined by a string alias from version 7 onwards, use the alternative contructor that specifies an alias")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public DataTypeDefinition(int parentId, Guid controlId)
|
||||
{
|
||||
_parentId = parentId;
|
||||
|
||||
_propertyEditorAlias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(controlId, false);
|
||||
if (_propertyEditorAlias == null)
|
||||
{
|
||||
//convert to Label!
|
||||
LogHelper.Warn<DataTypeDefinition>("Could not find a GUID -> Alias mapping for the legacy property editor with id " + controlId + ". The DataType has been converted to a Label.");
|
||||
_propertyEditorAlias = Constants.PropertyEditors.NoEditAlias;
|
||||
}
|
||||
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
public DataTypeDefinition(int parentId, string propertyEditorAlias)
|
||||
{
|
||||
_parentId = parentId;
|
||||
_propertyEditorAlias = propertyEditorAlias;
|
||||
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
public DataTypeDefinition(string propertyEditorAlias)
|
||||
{
|
||||
_parentId = -1;
|
||||
_propertyEditorAlias = propertyEditorAlias;
|
||||
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, string>(x => x.Name);
|
||||
public readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, int>(x => x.ParentId);
|
||||
public readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, int>(x => x.SortOrder);
|
||||
public readonly PropertyInfo LevelSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, int>(x => x.Level);
|
||||
public readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, string>(x => x.Path);
|
||||
public readonly PropertyInfo UserIdSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, int>(x => x.CreatorId);
|
||||
public readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, bool>(x => x.Trashed);
|
||||
public readonly PropertyInfo PropertyEditorAliasSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, string>(x => x.PropertyEditorAlias);
|
||||
public readonly PropertyInfo DatabaseTypeSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, DataTypeDatabaseType>(x => x.DatabaseType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Id of the Parent entity
|
||||
/// </summary>
|
||||
/// <remarks>Might not be necessary if handled as a relation?</remarks>
|
||||
[DataMember]
|
||||
public int ParentId
|
||||
{
|
||||
get { return _parentId; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _parentId, Ps.Value.ParentIdSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the current entity
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order of the content entity
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public int SortOrder
|
||||
{
|
||||
get { return _sortOrder; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the level of the content entity
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public int Level
|
||||
{
|
||||
get { return _level; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _level, Ps.Value.LevelSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Path //Setting this value should be handled by the class not the user
|
||||
{
|
||||
get { return _path; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _path, Ps.Value.PathSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id of the user who created this entity
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public int CreatorId
|
||||
{
|
||||
get { return _creatorId; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _creatorId, Ps.Value.UserIdSelector); }
|
||||
}
|
||||
|
||||
//NOTE: SD: Why do we have this ??
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether this entity is Trashed or not.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public bool Trashed
|
||||
{
|
||||
get { return _trashed; }
|
||||
internal set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _trashed, Ps.Value.TrashedSelector);
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["Trashed"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public string PropertyEditorAlias
|
||||
{
|
||||
get { return _propertyEditorAlias; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _propertyEditorAlias, Ps.Value.PropertyEditorAliasSelector);
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["DatabaseType"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id of the DataType control
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
[Obsolete("Property editor's are defined by a string alias from version 7 onwards, use the PropertyEditorAlias property instead. This method will return a generated GUID for any property editor alias not explicitly mapped to a legacy ID")]
|
||||
public Guid ControlId
|
||||
{
|
||||
get
|
||||
{
|
||||
return LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(
|
||||
_propertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId).Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(value, true);
|
||||
PropertyEditorAlias = alias;
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["ControlId"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the DatabaseType for which the DataType's value is saved as
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public DataTypeDatabaseType DatabaseType
|
||||
{
|
||||
get { return _databaseType; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _databaseType, Ps.Value.DatabaseTypeSelector);
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["DatabaseType"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IDictionary<string, object> _additionalData;
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IDictionary<string, object> IUmbracoEntity.AdditionalData
|
||||
{
|
||||
get { return _additionalData; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extensions methods for <see cref="IDataType"/>.
|
||||
/// </summary>
|
||||
public static class DataTypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the configuration object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The expected type of the configuration object.</typeparam>
|
||||
/// <param name="dataType">This datatype.</param>
|
||||
/// <exception cref="InvalidCastException">When the datatype configuration is not of the expected type.</exception>
|
||||
public static T ConfigurationAs<T>(this IDataType dataType)
|
||||
where T : class
|
||||
{
|
||||
if (dataType == null)
|
||||
throw new ArgumentNullException(nameof(dataType));
|
||||
|
||||
var configuration = dataType.Configuration;
|
||||
|
||||
switch (configuration)
|
||||
{
|
||||
case null:
|
||||
return null;
|
||||
case T configurationAsT:
|
||||
return configurationAsT;
|
||||
}
|
||||
|
||||
throw new InvalidCastException($"Cannot cast dataType configuration, of type {configuration.GetType().Name}, to {typeof(T).Name}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -23,7 +22,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public PropertyInfo PropertyInfo { get; private set; }
|
||||
public bool IsDeepCloneable { get; set; }
|
||||
public Type GenericListType { get; set; }
|
||||
public Type GenericListType { get; set; }
|
||||
public bool IsList
|
||||
{
|
||||
get { return GenericListType != null; }
|
||||
@@ -40,7 +39,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="output"></param>
|
||||
/// <returns></returns>
|
||||
/// <returns></returns>
|
||||
public static void DeepCloneRefProperties(IDeepCloneable input, IDeepCloneable output)
|
||||
{
|
||||
var inputType = input.GetType();
|
||||
@@ -68,7 +67,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (TypeHelper.IsTypeAssignableFrom<IDeepCloneable>(propertyInfo.PropertyType))
|
||||
{
|
||||
@@ -157,7 +156,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
else if (o is string || o.GetType().IsValueType)
|
||||
{
|
||||
//check if the item is a value type or a string, then we can just use it
|
||||
//check if the item is a value type or a string, then we can just use it
|
||||
newList.Add(o);
|
||||
}
|
||||
else
|
||||
@@ -197,4 +196,4 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,92 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Dictionary Item
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class DictionaryItem : EntityBase, IDictionaryItem
|
||||
{
|
||||
public Func<int, ILanguage> GetLanguage { get; set; }
|
||||
private Guid? _parentId;
|
||||
private string _itemKey;
|
||||
private IEnumerable<IDictionaryTranslation> _translations;
|
||||
|
||||
public DictionaryItem(string itemKey)
|
||||
: this(null, itemKey)
|
||||
{}
|
||||
|
||||
public DictionaryItem(Guid? parentId, string itemKey)
|
||||
{
|
||||
_parentId = parentId;
|
||||
_itemKey = itemKey;
|
||||
_translations = new List<IDictionaryTranslation>();
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo<DictionaryItem, Guid?>(x => x.ParentId);
|
||||
public readonly PropertyInfo ItemKeySelector = ExpressionHelper.GetPropertyInfo<DictionaryItem, string>(x => x.ItemKey);
|
||||
public readonly PropertyInfo TranslationsSelector = ExpressionHelper.GetPropertyInfo<DictionaryItem, IEnumerable<IDictionaryTranslation>>(x => x.Translations);
|
||||
|
||||
//Custom comparer for enumerable
|
||||
public readonly DelegateEqualityComparer<IEnumerable<IDictionaryTranslation>> DictionaryTranslationComparer =
|
||||
new DelegateEqualityComparer<IEnumerable<IDictionaryTranslation>>(
|
||||
(enumerable, translations) => enumerable.UnsortedSequenceEqual(translations),
|
||||
enumerable => enumerable.GetHashCode());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Parent Id of the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public Guid? ParentId
|
||||
{
|
||||
get { return _parentId; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _parentId, Ps.Value.ParentIdSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Key for the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string ItemKey
|
||||
{
|
||||
get { return _itemKey; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _itemKey, Ps.Value.ItemKeySelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of translations for the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public IEnumerable<IDictionaryTranslation> Translations
|
||||
{
|
||||
get { return _translations; }
|
||||
set
|
||||
{
|
||||
var asArray = value.ToArray();
|
||||
//ensure the language callback is set on each translation
|
||||
if (GetLanguage != null)
|
||||
{
|
||||
foreach (var translation in asArray.OfType<DictionaryTranslation>())
|
||||
{
|
||||
translation.GetLanguage = GetLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
SetPropertyValueAndDetectChanges(asArray, ref _translations, Ps.Value.TranslationsSelector,
|
||||
Ps.Value.DictionaryTranslationComparer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Dictionary Item
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class DictionaryItem : Entity, IDictionaryItem
|
||||
{
|
||||
public Func<int, ILanguage> GetLanguage { get; set; }
|
||||
private Guid? _parentId;
|
||||
private string _itemKey;
|
||||
private IEnumerable<IDictionaryTranslation> _translations;
|
||||
|
||||
public DictionaryItem(string itemKey)
|
||||
: this(null, itemKey)
|
||||
{}
|
||||
|
||||
public DictionaryItem(Guid? parentId, string itemKey)
|
||||
{
|
||||
_parentId = parentId;
|
||||
_itemKey = itemKey;
|
||||
_translations = new List<IDictionaryTranslation>();
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo<DictionaryItem, Guid?>(x => x.ParentId);
|
||||
public readonly PropertyInfo ItemKeySelector = ExpressionHelper.GetPropertyInfo<DictionaryItem, string>(x => x.ItemKey);
|
||||
public readonly PropertyInfo TranslationsSelector = ExpressionHelper.GetPropertyInfo<DictionaryItem, IEnumerable<IDictionaryTranslation>>(x => x.Translations);
|
||||
|
||||
//Custom comparer for enumerable
|
||||
public readonly DelegateEqualityComparer<IEnumerable<IDictionaryTranslation>> DictionaryTranslationComparer =
|
||||
new DelegateEqualityComparer<IEnumerable<IDictionaryTranslation>>(
|
||||
(enumerable, translations) => enumerable.UnsortedSequenceEqual(translations),
|
||||
enumerable => enumerable.GetHashCode());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Parent Id of the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public Guid? ParentId
|
||||
{
|
||||
get { return _parentId; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _parentId, Ps.Value.ParentIdSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Key for the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string ItemKey
|
||||
{
|
||||
get { return _itemKey; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _itemKey, Ps.Value.ItemKeySelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of translations for the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public IEnumerable<IDictionaryTranslation> Translations
|
||||
{
|
||||
get { return _translations; }
|
||||
set
|
||||
{
|
||||
var asArray = value.ToArray();
|
||||
//ensure the language callback is set on each translation
|
||||
if (GetLanguage != null)
|
||||
{
|
||||
foreach (var translation in asArray.OfType<DictionaryTranslation>())
|
||||
{
|
||||
translation.GetLanguage = GetLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
SetPropertyValueAndDetectChanges(asArray, ref _translations, Ps.Value.TranslationsSelector,
|
||||
Ps.Value.DictionaryTranslationComparer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public static class DictionaryItemExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the translation value for the language id, if no translation is found it returns an empty string
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <param name="languageId"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetTranslatedValue(this IDictionaryItem d, int languageId)
|
||||
{
|
||||
var trans = d.Translations.FirstOrDefault(x => x.LanguageId == languageId);
|
||||
return trans == null ? string.Empty : trans.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the default translated value based on the default language
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetDefaultValue(this IDictionaryItem d)
|
||||
{
|
||||
var defaultTranslation = d.Translations.FirstOrDefault(x => x.Language.Id == 1);
|
||||
return defaultTranslation == null ? string.Empty : defaultTranslation.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +1,126 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a translation for a <see cref="DictionaryItem"/>
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class DictionaryTranslation : EntityBase, IDictionaryTranslation
|
||||
{
|
||||
internal Func<int, ILanguage> GetLanguage { get; set; }
|
||||
|
||||
private ILanguage _language;
|
||||
private string _value;
|
||||
//note: this will be memberwise cloned
|
||||
private int _languageId;
|
||||
|
||||
public DictionaryTranslation(ILanguage language, string value)
|
||||
{
|
||||
if (language == null) throw new ArgumentNullException("language");
|
||||
_language = language;
|
||||
_languageId = _language.Id;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public DictionaryTranslation(ILanguage language, string value, Guid uniqueId)
|
||||
{
|
||||
if (language == null) throw new ArgumentNullException("language");
|
||||
_language = language;
|
||||
_languageId = _language.Id;
|
||||
_value = value;
|
||||
Key = uniqueId;
|
||||
}
|
||||
|
||||
internal DictionaryTranslation(int languageId, string value)
|
||||
{
|
||||
_languageId = languageId;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
internal DictionaryTranslation(int languageId, string value, Guid uniqueId)
|
||||
{
|
||||
_languageId = languageId;
|
||||
_value = value;
|
||||
Key = uniqueId;
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo<DictionaryTranslation, ILanguage>(x => x.Language);
|
||||
public readonly PropertyInfo ValueSelector = ExpressionHelper.GetPropertyInfo<DictionaryTranslation, string>(x => x.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Language"/> for the translation
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Marked as DoNotClone - TODO: this member shouldn't really exist here in the first place, the DictionaryItem
|
||||
/// class will have a deep hierarchy of objects which all get deep cloned which we don't want. This should have simply
|
||||
/// just referenced a language ID not the actual language object. In v8 we need to fix this.
|
||||
/// We're going to have to do the same hacky stuff we had to do with the Template/File contents so that this is returned
|
||||
/// on a callback.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
[DoNotClone]
|
||||
public ILanguage Language
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_language != null)
|
||||
return _language;
|
||||
|
||||
// else, must lazy-load
|
||||
if (GetLanguage != null && _languageId > 0)
|
||||
_language = GetLanguage(_languageId);
|
||||
return _language;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _language, Ps.Value.LanguageSelector);
|
||||
_languageId = _language == null ? -1 : _language.Id;
|
||||
}
|
||||
}
|
||||
|
||||
public int LanguageId
|
||||
{
|
||||
get { return _languageId; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the translated text
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Value
|
||||
{
|
||||
get { return _value; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _value, Ps.Value.ValueSelector); }
|
||||
}
|
||||
|
||||
protected override void PerformDeepClone(object clone)
|
||||
{
|
||||
base.PerformDeepClone(clone);
|
||||
|
||||
var clonedEntity = (DictionaryTranslation)clone;
|
||||
|
||||
// clear fields that were memberwise-cloned and that we don't want to clone
|
||||
clonedEntity._language = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a translation for a <see cref="DictionaryItem"/>
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class DictionaryTranslation : Entity, IDictionaryTranslation
|
||||
{
|
||||
internal Func<int, ILanguage> GetLanguage { get; set; }
|
||||
|
||||
private ILanguage _language;
|
||||
private string _value;
|
||||
//note: this will be memberwise cloned
|
||||
private int _languageId;
|
||||
|
||||
public DictionaryTranslation(ILanguage language, string value)
|
||||
{
|
||||
if (language == null) throw new ArgumentNullException("language");
|
||||
_language = language;
|
||||
_languageId = _language.Id;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public DictionaryTranslation(ILanguage language, string value, Guid uniqueId)
|
||||
{
|
||||
if (language == null) throw new ArgumentNullException("language");
|
||||
_language = language;
|
||||
_languageId = _language.Id;
|
||||
_value = value;
|
||||
Key = uniqueId;
|
||||
}
|
||||
|
||||
internal DictionaryTranslation(int languageId, string value)
|
||||
{
|
||||
_languageId = languageId;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
internal DictionaryTranslation(int languageId, string value, Guid uniqueId)
|
||||
{
|
||||
_languageId = languageId;
|
||||
_value = value;
|
||||
Key = uniqueId;
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo<DictionaryTranslation, ILanguage>(x => x.Language);
|
||||
public readonly PropertyInfo ValueSelector = ExpressionHelper.GetPropertyInfo<DictionaryTranslation, string>(x => x.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Language"/> for the translation
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Marked as DoNotClone - TODO: this member shouldn't really exist here in the first place, the DictionaryItem
|
||||
/// class will have a deep hierarchy of objects which all get deep cloned which we don't want. This should have simply
|
||||
/// just referenced a language ID not the actual language object. In v8 we need to fix this.
|
||||
/// We're going to have to do the same hacky stuff we had to do with the Template/File contents so that this is returned
|
||||
/// on a callback.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
[DoNotClone]
|
||||
public ILanguage Language
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_language != null)
|
||||
return _language;
|
||||
|
||||
// else, must lazy-load
|
||||
if (GetLanguage != null && _languageId > 0)
|
||||
_language = GetLanguage(_languageId);
|
||||
return _language;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _language, Ps.Value.LanguageSelector);
|
||||
_languageId = _language == null ? -1 : _language.Id;
|
||||
}
|
||||
}
|
||||
|
||||
public int LanguageId
|
||||
{
|
||||
get { return _languageId; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the translated text
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Value
|
||||
{
|
||||
get { return _value; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _value, Ps.Value.ValueSelector); }
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (DictionaryTranslation)base.DeepClone();
|
||||
|
||||
// clear fields that were memberwise-cloned and that we don't want to clone
|
||||
clone._language = null;
|
||||
|
||||
// turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
|
||||
// this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
// re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -7,17 +7,17 @@ namespace Umbraco.Core.Models
|
||||
/// that should be ignored for cloning when using the DeepCloneHelper
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
///
|
||||
/// This attribute must be used:
|
||||
/// * when the property is backed by a field but the result of the property is the un-natural data stored in the field
|
||||
///
|
||||
///
|
||||
/// This attribute should not be used:
|
||||
/// * when the property is virtual
|
||||
/// * when the setter performs additional required logic other than just setting the underlying field
|
||||
///
|
||||
///
|
||||
/// </remarks>
|
||||
internal class DoNotCloneAttribute : Attribute
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents data that has been submitted to be saved for a content property
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This object exists because we may need to save additional data for each property, more than just
|
||||
/// the string representation of the value being submitted. An example of this is uploaded files.
|
||||
/// </remarks>
|
||||
public class ContentPropertyData
|
||||
{
|
||||
public ContentPropertyData(object value, object dataTypeConfiguration)
|
||||
{
|
||||
Value = value;
|
||||
DataTypeConfiguration = dataTypeConfiguration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value submitted for the property
|
||||
/// </summary>
|
||||
public object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The data type configuration for the property.
|
||||
/// </summary>
|
||||
public object DataTypeConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier of the content owning the property.
|
||||
/// </summary>
|
||||
public Guid ContentKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier of the property type.
|
||||
/// </summary>
|
||||
public Guid PropertyTypeKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the uploaded files.
|
||||
/// </summary>
|
||||
public ContentPropertyFile[] Files { get; set; }
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Models.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents data that has been submitted to be saved for a content property
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This object exists because we may need to save additional data for each property, more than just
|
||||
/// the string representation of the value being submitted. An example of this is uploaded files.
|
||||
/// </remarks>
|
||||
public class ContentPropertyData
|
||||
{
|
||||
public ContentPropertyData(object value, PreValueCollection preValues)
|
||||
: this(value, preValues, new Dictionary<string, object>())
|
||||
{
|
||||
}
|
||||
|
||||
public ContentPropertyData(object value, PreValueCollection preValues, IDictionary<string, object> additionalData)
|
||||
{
|
||||
Value = value;
|
||||
PreValues = preValues;
|
||||
AdditionalData = new ReadOnlyDictionary<string, object>(additionalData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value submitted for the property
|
||||
/// </summary>
|
||||
public object Value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The pre-value collection for the content property
|
||||
/// </summary>
|
||||
public PreValueCollection PreValues { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary containing any additional objects that are related to this property when saving
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<string, object> AdditionalData { get; private set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
namespace Umbraco.Core.Models.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an uploaded file for a property.
|
||||
/// </summary>
|
||||
public class ContentPropertyFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the property alias.
|
||||
/// </summary>
|
||||
public string PropertyAlias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When dealing with content variants, this is the culture for the variant
|
||||
/// </summary>
|
||||
public string Culture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An array of metadata that is parsed out from the file info posted to the server which is set on the client.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can be used for property types like Nested Content that need to have special unique identifiers for each file since there might be multiple files
|
||||
/// per property.
|
||||
/// </remarks>
|
||||
public string[] Metadata { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the file.
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the temporary path where the file has been uploaded.
|
||||
/// </summary>
|
||||
public string TempFilePath { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a concrete implementation of <see cref="BeingDirtyBase"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This class is provided for classes that cannot inherit from <see cref="BeingDirtyBase"/>
|
||||
/// and therefore need to implement <see cref="IRememberBeingDirty"/>, by re-using some of
|
||||
/// <see cref="BeingDirtyBase"/> logic.</para>
|
||||
/// </remarks>
|
||||
public sealed class BeingDirty : BeingDirtyBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets a property value, detects changes and manages the dirty flag.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the value.</typeparam>
|
||||
/// <param name="value">The new value.</param>
|
||||
/// <param name="valueRef">A reference to the value to set.</param>
|
||||
/// <param name="propertySelector">The property selector.</param>
|
||||
/// <param name="comparer">A comparer to compare property values.</param>
|
||||
public new void SetPropertyValueAndDetectChanges<T>(T value, ref T valueRef, PropertyInfo propertySelector, IEqualityComparer<T> comparer = null)
|
||||
{
|
||||
base.SetPropertyValueAndDetectChanges(value, ref valueRef, propertySelector, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers that a property has changed.
|
||||
/// </summary>
|
||||
public new void OnPropertyChanged(PropertyInfo propertySelector)
|
||||
{
|
||||
base.OnPropertyChanged(propertySelector);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base implementation of <see cref="ICanBeDirty"/> and <see cref="IRememberBeingDirty"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class BeingDirtyBase : IRememberBeingDirty
|
||||
{
|
||||
private bool _withChanges = true; // should we track changes?
|
||||
private Dictionary<string, bool> _currentChanges; // which properties have changed?
|
||||
private Dictionary<string, bool> _savedChanges; // which properties had changed at last commit?
|
||||
|
||||
#region ICanBeDirty
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool IsDirty()
|
||||
{
|
||||
return _currentChanges != null && _currentChanges.Any();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool IsPropertyDirty(string propertyName)
|
||||
{
|
||||
return _currentChanges != null && _currentChanges.Any(x => x.Key == propertyName);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<string> GetDirtyProperties()
|
||||
{
|
||||
// ReSharper disable once MergeConditionalExpression
|
||||
return _currentChanges == null
|
||||
? Enumerable.Empty<string>()
|
||||
: _currentChanges.Where(x => x.Value).Select(x => x.Key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>Saves dirty properties so they can be checked with WasDirty.</remarks>
|
||||
public virtual void ResetDirtyProperties()
|
||||
{
|
||||
ResetDirtyProperties(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IRememberBeingDirty
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool WasDirty()
|
||||
{
|
||||
return _savedChanges != null && _savedChanges.Any();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool WasPropertyDirty(string propertyName)
|
||||
{
|
||||
return _savedChanges != null && _savedChanges.Any(x => x.Key == propertyName);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ResetWereDirtyProperties()
|
||||
{
|
||||
// note: cannot .Clear() because when memberwise-cloning this will be the SAME
|
||||
// instance as the one on the clone, so we need to create a new instance.
|
||||
_savedChanges = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ResetDirtyProperties(bool rememberDirty)
|
||||
{
|
||||
// capture changes if remembering
|
||||
// clone the dictionary in case it's shared by an entity clone
|
||||
_savedChanges = rememberDirty && _currentChanges != null
|
||||
? _currentChanges.ToDictionary(v => v.Key, v => v.Value)
|
||||
: null;
|
||||
|
||||
// note: cannot .Clear() because when memberwise-clone this will be the SAME
|
||||
// instance as the one on the clone, so we need to create a new instance.
|
||||
_currentChanges = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<string> GetWereDirtyProperties()
|
||||
{
|
||||
// ReSharper disable once MergeConditionalExpression
|
||||
return _savedChanges == null
|
||||
? Enumerable.Empty<string>()
|
||||
: _savedChanges.Where(x => x.Value).Select(x => x.Key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Change Tracking
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a property changes.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Registers that a property has changed.
|
||||
/// </summary>
|
||||
protected virtual void OnPropertyChanged(PropertyInfo propertyInfo)
|
||||
{
|
||||
if (_withChanges == false)
|
||||
return;
|
||||
|
||||
if (_currentChanges == null)
|
||||
_currentChanges = new Dictionary<string, bool>();
|
||||
|
||||
_currentChanges[propertyInfo.Name] = true;
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyInfo.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables change tracking.
|
||||
/// </summary>
|
||||
public void DisableChangeTracking()
|
||||
{
|
||||
_withChanges = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables change tracking.
|
||||
/// </summary>
|
||||
public void EnableChangeTracking()
|
||||
{
|
||||
_withChanges = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a property value, detects changes and manages the dirty flag.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the value.</typeparam>
|
||||
/// <param name="value">The new value.</param>
|
||||
/// <param name="valueRef">A reference to the value to set.</param>
|
||||
/// <param name="propertySelector">The property selector.</param>
|
||||
/// <param name="comparer">A comparer to compare property values.</param>
|
||||
protected void SetPropertyValueAndDetectChanges<T>(T value, ref T valueRef, PropertyInfo propertySelector, IEqualityComparer<T> comparer = null)
|
||||
{
|
||||
if (comparer == null)
|
||||
{
|
||||
// if no comparer is provided, use the default provider, as long as the value is not
|
||||
// an IEnumerable - exclude strings, which are IEnumerable but have a default comparer
|
||||
var typeofT = typeof(T);
|
||||
if (!(typeofT == typeof(string)) && typeof(IEnumerable).IsAssignableFrom(typeofT))
|
||||
throw new ArgumentNullException(nameof(comparer), "A custom comparer must be supplied for IEnumerable values.");
|
||||
comparer = EqualityComparer<T>.Default;
|
||||
}
|
||||
|
||||
// compare values
|
||||
var changed = _withChanges && comparer.Equals(valueRef, value) == false;
|
||||
|
||||
// assign the new value
|
||||
valueRef = value;
|
||||
|
||||
// handle change
|
||||
if (changed)
|
||||
OnPropertyChanged(propertySelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects changes and manages the dirty flag.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the value.</typeparam>
|
||||
/// <param name="value">The new value.</param>
|
||||
/// <param name="orig">The original value.</param>
|
||||
/// <param name="propertySelector">The property selector.</param>
|
||||
/// <param name="comparer">A comparer to compare property values.</param>
|
||||
/// <param name="changed">A value indicating whether we know values have changed and no comparison is required.</param>
|
||||
protected void DetectChanges<T>(T value, T orig, PropertyInfo propertySelector, IEqualityComparer<T> comparer, bool changed)
|
||||
{
|
||||
// compare values
|
||||
changed = _withChanges && (changed || !comparer.Equals(orig, value));
|
||||
|
||||
// handle change
|
||||
if (changed)
|
||||
OnPropertyChanged(propertySelector);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IContentEntitySlim"/>.
|
||||
/// </summary>
|
||||
public class ContentEntitySlim : EntitySlim, IContentEntitySlim
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ContentTypeAlias { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ContentTypeIcon { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ContentTypeThumbnail { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IDocumentEntitySlim"/>.
|
||||
/// </summary>
|
||||
public class DocumentEntitySlim : ContentEntitySlim, IDocumentEntitySlim
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<string, string> Empty = new Dictionary<string, string>();
|
||||
|
||||
private IReadOnlyDictionary<string, string> _cultureNames;
|
||||
private IEnumerable<string> _publishedCultures;
|
||||
private IEnumerable<string> _editedCultures;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyDictionary<string, string> CultureNames
|
||||
{
|
||||
get => _cultureNames ?? Empty;
|
||||
set => _cultureNames = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> PublishedCultures
|
||||
{
|
||||
get => _publishedCultures ?? Enumerable.Empty<string>();
|
||||
set => _publishedCultures = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> EditedCultures
|
||||
{
|
||||
get => _editedCultures ?? Enumerable.Empty<string>();
|
||||
set => _editedCultures = value;
|
||||
}
|
||||
|
||||
public ContentVariation Variations { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Published { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Edited { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for entities.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
[DebuggerDisplay("Id: {" + nameof(Id) + "}")]
|
||||
public abstract class EntityBase : BeingDirtyBase, IEntity
|
||||
{
|
||||
#if DEBUG_MODEL
|
||||
public Guid InstanceId = Guid.NewGuid();
|
||||
#endif
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private bool _hasIdentity;
|
||||
private int _id;
|
||||
private Guid _key;
|
||||
private DateTime _createDate;
|
||||
private DateTime _updateDate;
|
||||
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo<EntityBase, int>(x => x.Id);
|
||||
public readonly PropertyInfo KeySelector = ExpressionHelper.GetPropertyInfo<EntityBase, Guid>(x => x.Key);
|
||||
public readonly PropertyInfo CreateDateSelector = ExpressionHelper.GetPropertyInfo<EntityBase, DateTime>(x => x.CreateDate);
|
||||
public readonly PropertyInfo UpdateDateSelector = ExpressionHelper.GetPropertyInfo<EntityBase, DateTime>(x => x.UpdateDate);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int Id
|
||||
{
|
||||
get => _id;
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _id, Ps.Value.IdSelector);
|
||||
_hasIdentity = value != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public Guid Key
|
||||
{
|
||||
get
|
||||
{
|
||||
// if an entity does NOT have a key yet, assign one now
|
||||
if (_key == Guid.Empty)
|
||||
_key = Guid.NewGuid();
|
||||
return _key;
|
||||
}
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _key, Ps.Value.KeySelector);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public DateTime CreateDate
|
||||
{
|
||||
get => _createDate;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _createDate, Ps.Value.CreateDateSelector);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public DateTime UpdateDate
|
||||
{
|
||||
get => _updateDate;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _updateDate, Ps.Value.UpdateDateSelector);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public DateTime? DeleteDate { get; set; } // no change tracking - not persisted
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public virtual bool HasIdentity => _hasIdentity;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the entity identity.
|
||||
/// </summary>
|
||||
internal virtual void ResetIdentity()
|
||||
{
|
||||
_id = default;
|
||||
_key = Guid.Empty;
|
||||
_hasIdentity = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the entity when it is being saved for the first time.
|
||||
/// </summary>
|
||||
internal virtual void AddingEntity()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
|
||||
// set the create and update dates, if not already set
|
||||
if (IsPropertyDirty("CreateDate") == false || _createDate == default)
|
||||
CreateDate = now;
|
||||
if (IsPropertyDirty("UpdateDate") == false || _updateDate == default)
|
||||
UpdateDate = now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the entity when it is being saved.
|
||||
/// </summary>
|
||||
internal virtual void UpdatingEntity()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
|
||||
// just in case
|
||||
if (_createDate == default)
|
||||
CreateDate = now;
|
||||
|
||||
// set the update date if not already set
|
||||
if (IsPropertyDirty("UpdateDate") == false || _updateDate == default)
|
||||
UpdateDate = now;
|
||||
}
|
||||
|
||||
public virtual bool Equals(EntityBase other)
|
||||
{
|
||||
return other != null && (ReferenceEquals(this, other) || SameIdentityAs(other));
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj != null && (ReferenceEquals(this, obj) || SameIdentityAs(obj as EntityBase));
|
||||
}
|
||||
|
||||
private bool SameIdentityAs(EntityBase other)
|
||||
{
|
||||
if (other == null) return false;
|
||||
|
||||
// same identity if
|
||||
// - same object (reference equals)
|
||||
// - or same Clr type, both have identities, and they are identical
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
return true;
|
||||
|
||||
return GetType() == other.GetType() && HasIdentity && other.HasIdentity && Id == other.Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = HasIdentity.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Id;
|
||||
hashCode = (hashCode * 397) ^ GetType().GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
// memberwise-clone (ie shallow clone) the entity
|
||||
var unused = Key; // ensure that 'this' has a key, before cloning
|
||||
var clone = (EntityBase) MemberwiseClone();
|
||||
|
||||
#if DEBUG_MODEL
|
||||
clone.InstanceId = Guid.NewGuid();
|
||||
#endif
|
||||
|
||||
//disable change tracking while we deep clone IDeepCloneable properties
|
||||
clone.DisableChangeTracking();
|
||||
|
||||
// deep clone ref properties that are IDeepCloneable
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
|
||||
PerformDeepClone(clone);
|
||||
|
||||
// clear changes (ensures the clone has its own dictionaries)
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
//re-enable change tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by inheritors to modify the DeepCloning logic
|
||||
/// </summary>
|
||||
/// <param name="clone"></param>
|
||||
protected virtual void PerformDeepClone(object clone)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
// fixme - changing the name of some properties that were in additionalData => must update corresponding javascript?
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="IEntitySlim"/> for internal use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Although it implements <see cref="IEntitySlim"/>, this class does not
|
||||
/// implement <see cref="IRememberBeingDirty"/> and everything this interface defines, throws.</para>
|
||||
/// <para>Although it implements <see cref="IEntitySlim"/>, this class does not
|
||||
/// implement <see cref="IDeepCloneable"/> and deep-cloning throws.</para>
|
||||
/// </remarks>
|
||||
public class EntitySlim : IEntitySlim
|
||||
{
|
||||
private IDictionary<string, object> _additionalData;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an entity representing "root".
|
||||
/// </summary>
|
||||
public static readonly IEntitySlim Root = new EntitySlim { Path = "-1", Name = "root", HasChildren = true };
|
||||
|
||||
// implement IEntity
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public Guid Key { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public DateTime UpdateDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public DateTime? DeleteDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public bool HasIdentity => Id != 0;
|
||||
|
||||
|
||||
// implement ITreeEntity
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int CreatorId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int ParentId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetParent(ITreeEntity parent) => throw new WontImplementException();
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int Level { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public bool Trashed { get; set; }
|
||||
|
||||
|
||||
// implement IUmbracoEntity
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public IDictionary<string, object> AdditionalData => _additionalData ?? (_additionalData = new Dictionary<string, object>());
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
public bool HasAdditionalData => _additionalData != null;
|
||||
|
||||
|
||||
// implement IEntitySlim
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public Guid NodeObjectType { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public bool HasChildren { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public virtual bool IsContainer { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a lightweight property.
|
||||
/// </summary>
|
||||
public class PropertySlim
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertySlim"/> class.
|
||||
/// </summary>
|
||||
public PropertySlim(string editorAlias, object value)
|
||||
{
|
||||
PropertyEditorAlias = editorAlias;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property editor alias.
|
||||
/// </summary>
|
||||
public string PropertyEditorAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value.
|
||||
/// </summary>
|
||||
public object Value { get; }
|
||||
|
||||
protected bool Equals(PropertySlim other)
|
||||
{
|
||||
return PropertyEditorAlias.Equals(other.PropertyEditorAlias) && Equals(Value, other.Value);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != GetType()) return false;
|
||||
return Equals((PropertySlim) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (PropertyEditorAlias.GetHashCode() * 397) ^ (Value != null ? Value.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IDeepCloneable
|
||||
|
||||
/// <inheritdoc />
|
||||
public object DeepClone()
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IRememberBeingDirty
|
||||
|
||||
// IEntitySlim does *not* track changes, but since it indirectly implements IUmbracoEntity,
|
||||
// and therefore IRememberBeingDirty, we have to have those methods - which all throw.
|
||||
|
||||
public bool IsDirty()
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
public bool IsPropertyDirty(string propName)
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetDirtyProperties()
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
public void ResetDirtyProperties()
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
public bool WasDirty()
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
public bool WasPropertyDirty(string propertyName)
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
public void ResetWereDirtyProperties()
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
public void ResetDirtyProperties(bool rememberDirty)
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetWereDirtyProperties()
|
||||
{
|
||||
throw new WontImplementException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an entity that tracks property changes and can be dirty.
|
||||
/// </summary>
|
||||
public interface ICanBeDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the current entity is dirty.
|
||||
/// </summary>
|
||||
bool IsDirty();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a specific property is dirty.
|
||||
/// </summary>
|
||||
bool IsPropertyDirty(string propName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets properties that are dirty.
|
||||
/// </summary>
|
||||
IEnumerable<string> GetDirtyProperties();
|
||||
|
||||
/// <summary>
|
||||
/// Resets dirty properties.
|
||||
/// </summary>
|
||||
void ResetDirtyProperties();
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a lightweight content entity, managed by the entity service.
|
||||
/// </summary>
|
||||
public interface IContentEntitySlim : IEntitySlim
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the content type alias.
|
||||
/// </summary>
|
||||
string ContentTypeAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type icon.
|
||||
/// </summary>
|
||||
string ContentTypeIcon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type thumbnail.
|
||||
/// </summary>
|
||||
string ContentTypeThumbnail { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a lightweight document entity, managed by the entity service.
|
||||
/// </summary>
|
||||
public interface IDocumentEntitySlim : IContentEntitySlim
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the variant name for each culture
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<string, string> CultureNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the published cultures.
|
||||
/// </summary>
|
||||
IEnumerable<string> PublishedCultures { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the edited cultures.
|
||||
/// </summary>
|
||||
IEnumerable<string> EditedCultures { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content variation of the content type.
|
||||
/// </summary>
|
||||
ContentVariation Variations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is published.
|
||||
/// </summary>
|
||||
bool Published { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content has been edited.
|
||||
/// </summary>
|
||||
bool Edited { get; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an entity.
|
||||
/// </summary>
|
||||
public interface IEntity : IDeepCloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the integer identifier of the entity.
|
||||
/// </summary>
|
||||
int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Guid unique identifier of the entity.
|
||||
/// </summary>
|
||||
Guid Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the creation date.
|
||||
/// </summary>
|
||||
DateTime CreateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last update date.
|
||||
/// </summary>
|
||||
DateTime UpdateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delete date.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The delete date is null when the entity has not been deleted.</para>
|
||||
/// <para>The delete date has a value when the entity instance has been deleted, but this value
|
||||
/// is transient and not persisted in database (since the entity does not exist anymore).</para>
|
||||
/// </remarks>
|
||||
DateTime? DeleteDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the entity has an identity.
|
||||
/// </summary>
|
||||
bool HasIdentity { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a lightweight entity, managed by the entity service.
|
||||
/// </summary>
|
||||
public interface IEntitySlim : IUmbracoEntity, IHaveAdditionalData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the entity object type.
|
||||
/// </summary>
|
||||
Guid NodeObjectType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has children.
|
||||
/// </summary>
|
||||
bool HasChildren { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the entity is a container.
|
||||
/// </summary>
|
||||
bool IsContainer { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides support for additional data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Additional data are transient, not deep-cloned.</para>
|
||||
/// </remarks>
|
||||
public interface IHaveAdditionalData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets additional data for this entity.
|
||||
/// </summary>
|
||||
/// <remarks>Can be empty, but never null. To avoid allocating, do not
|
||||
/// test for emptyness, but use <see cref="HasAdditionalData"/> instead.</remarks>
|
||||
IDictionary<string, object> AdditionalData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this entity has additional data.
|
||||
/// </summary>
|
||||
/// <remarks>Use this property to check for additional data without
|
||||
/// getting <see cref="AdditionalData"/>, to avoid allocating.</remarks>
|
||||
bool HasAdditionalData { get; }
|
||||
|
||||
// how to implement:
|
||||
|
||||
/*
|
||||
private IDictionary<string, object> _additionalData;
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
[DoNotClone]
|
||||
PublicAccessEntry IDictionary<string, object> AdditionalData => _additionalData ?? (_additionalData = new Dictionary<string, object>());
|
||||
|
||||
/// <inheritdoc />
|
||||
[IgnoreDataMember]
|
||||
PublicAccessEntry bool HasAdditionalData => _additionalData != null;
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an entity that tracks property changes and can be dirty, and remembers
|
||||
/// which properties were dirty when the changes were committed.
|
||||
/// </summary>
|
||||
public interface IRememberBeingDirty : ICanBeDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the current entity is dirty.
|
||||
/// </summary>
|
||||
/// <remarks>A property was dirty if it had been changed and the changes were committed.</remarks>
|
||||
bool WasDirty();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a specific property was dirty.
|
||||
/// </summary>
|
||||
/// <remarks>A property was dirty if it had been changed and the changes were committed.</remarks>
|
||||
bool WasPropertyDirty(string propertyName);
|
||||
|
||||
/// <summary>
|
||||
/// Resets properties that were dirty.
|
||||
/// </summary>
|
||||
void ResetWereDirtyProperties();
|
||||
|
||||
/// <summary>
|
||||
/// Resets dirty properties.
|
||||
/// </summary>
|
||||
/// <param name="rememberDirty">A value indicating whether to remember dirty properties.</param>
|
||||
/// <remarks>When <paramref name="rememberDirty"/> is true, dirty properties are saved so they can be checked with WasDirty.</remarks>
|
||||
void ResetDirtyProperties(bool rememberDirty);
|
||||
|
||||
/// <summary>
|
||||
/// Gets properties that were dirty.
|
||||
/// </summary>
|
||||
IEnumerable<string> GetWereDirtyProperties();
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an entity that belongs to a tree.
|
||||
/// </summary>
|
||||
public interface ITreeEntity : IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the entity.
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the user who created this entity.
|
||||
/// </summary>
|
||||
int CreatorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the parent entity.
|
||||
/// </summary>
|
||||
int ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the parent entity.
|
||||
/// </summary>
|
||||
/// <remarks>Use this method to set the parent entity when the parent entity is known, but has not
|
||||
/// been persistent and does not yet have an identity. The parent identifier will we retrieved
|
||||
/// from the parent entity when needed. If the parent entity still does not have an entity by that
|
||||
/// time, an exception will be thrown by <see cref="ParentId"/> getter.</remarks>
|
||||
void SetParent(ITreeEntity parent);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the level of the entity.
|
||||
/// </summary>
|
||||
int Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the entity.
|
||||
/// </summary>
|
||||
string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order of the entity.
|
||||
/// </summary>
|
||||
int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this entity is trashed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Trashed entities are located in the recycle bin.</para>
|
||||
/// <para>Always false for entities that do not support being trashed.</para>
|
||||
/// </remarks>
|
||||
bool Trashed { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entity that can be managed by the entity service.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>An IUmbracoEntity can be related to another via the IRelationService.</para>
|
||||
/// <para>IUmbracoEntities can be retrieved with the IEntityService.</para>
|
||||
/// <para>An IUmbracoEntity can participate in notifications.</para>
|
||||
/// </remarks>
|
||||
public interface IUmbracoEntity : ITreeEntity, IRememberBeingDirty
|
||||
{ }
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for tree entities.
|
||||
/// </summary>
|
||||
public abstract class TreeEntityBase : EntityBase, ITreeEntity
|
||||
{
|
||||
private static PropertySelectors _selectors;
|
||||
private static PropertySelectors Selectors => _selectors ?? (_selectors = new PropertySelectors());
|
||||
|
||||
private string _name;
|
||||
private int _creatorId;
|
||||
private int _parentId;
|
||||
private bool _hasParentId;
|
||||
private ITreeEntity _parent;
|
||||
private int _level;
|
||||
private string _path;
|
||||
private int _sortOrder;
|
||||
private bool _trashed;
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo Name = ExpressionHelper.GetPropertyInfo<ContentBase, string>(x => x.Name);
|
||||
public readonly PropertyInfo CreatorId = ExpressionHelper.GetPropertyInfo<ContentBase, int>(x => x.CreatorId);
|
||||
public readonly PropertyInfo ParentId = ExpressionHelper.GetPropertyInfo<ContentBase, int>(x => x.ParentId);
|
||||
public readonly PropertyInfo Level = ExpressionHelper.GetPropertyInfo<ContentBase, int>(x => x.Level);
|
||||
public readonly PropertyInfo Path = ExpressionHelper.GetPropertyInfo<ContentBase, string>(x => x.Path);
|
||||
public readonly PropertyInfo SortOrder = ExpressionHelper.GetPropertyInfo<ContentBase, int>(x => x.SortOrder);
|
||||
public readonly PropertyInfo Trashed = ExpressionHelper.GetPropertyInfo<ContentBase, bool>(x => x.Trashed);
|
||||
}
|
||||
|
||||
// fixme
|
||||
// ParentId, Path, Level and Trashed all should be consistent, and all derive from parentId, really
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _name, Selectors.Name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int CreatorId
|
||||
{
|
||||
get => _creatorId;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _creatorId, Selectors.CreatorId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int ParentId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_hasParentId) return _parentId;
|
||||
|
||||
if (_parent == null) throw new InvalidOperationException("Content does not have a parent.");
|
||||
if (!_parent.HasIdentity) throw new InvalidOperationException("Content's parent does not have an identity.");
|
||||
|
||||
_parentId = _parent.Id;
|
||||
if (_parentId == 0)
|
||||
throw new Exception("Panic: parent has an identity but id is zero.");
|
||||
|
||||
_hasParentId = true;
|
||||
_parent = null;
|
||||
return _parentId;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == 0)
|
||||
throw new ArgumentException("Value cannot be zero.", nameof(value));
|
||||
SetPropertyValueAndDetectChanges(value, ref _parentId, Selectors.ParentId);
|
||||
_hasParentId = true;
|
||||
_parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetParent(ITreeEntity parent)
|
||||
{
|
||||
_hasParentId = false;
|
||||
_parent = parent;
|
||||
OnPropertyChanged(Selectors.ParentId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int Level
|
||||
{
|
||||
get => _level;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _level, Selectors.Level);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public string Path
|
||||
{
|
||||
get => _path;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _path, Selectors.Path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int SortOrder
|
||||
{
|
||||
get => _sortOrder;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _sortOrder, Selectors.SortOrder);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public bool Trashed
|
||||
{
|
||||
get => _trashed;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _trashed, Selectors.Trashed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the path of a tree entity.
|
||||
/// </summary>
|
||||
public class TreeEntityPath
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the entity.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path of the entity.
|
||||
/// </summary>
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Base Abstract Entity
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
[DebuggerDisplay("Id: {Id}")]
|
||||
public abstract class Entity : TracksChangesEntityBase, IEntity, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
private bool _hasIdentity;
|
||||
private int _id;
|
||||
private Guid _key;
|
||||
private DateTime _createDate;
|
||||
private DateTime _updateDate;
|
||||
private bool _wasCancelled;
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo<Entity, int>(x => x.Id);
|
||||
public readonly PropertyInfo KeySelector = ExpressionHelper.GetPropertyInfo<Entity, Guid>(x => x.Key);
|
||||
public readonly PropertyInfo CreateDateSelector = ExpressionHelper.GetPropertyInfo<Entity, DateTime>(x => x.CreateDate);
|
||||
public readonly PropertyInfo UpdateDateSelector = ExpressionHelper.GetPropertyInfo<Entity, DateTime>(x => x.UpdateDate);
|
||||
public readonly PropertyInfo HasIdentitySelector = ExpressionHelper.GetPropertyInfo<Entity, bool>(x => x.HasIdentity);
|
||||
public readonly PropertyInfo WasCancelledSelector = ExpressionHelper.GetPropertyInfo<Entity, bool>(x => x.WasCancelled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Integer Id
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public int Id
|
||||
{
|
||||
get { return _id; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _id, Ps.Value.IdSelector);
|
||||
HasIdentity = true; //set the has Identity
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Guid based Id
|
||||
/// </summary>
|
||||
/// <remarks>The key is currectly used to store the Unique Id from the
|
||||
/// umbracoNode table, which many of the entities are based on.</remarks>
|
||||
[DataMember]
|
||||
public Guid Key
|
||||
{
|
||||
get
|
||||
{
|
||||
// if an entity does NOT have a UniqueId yet, assign one now
|
||||
if (_key == Guid.Empty)
|
||||
_key = Guid.NewGuid();
|
||||
return _key;
|
||||
}
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _key, Ps.Value.KeySelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Created Date
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public DateTime CreateDate
|
||||
{
|
||||
get { return _createDate; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _createDate, Ps.Value.CreateDateSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WasCancelled flag, which is used to track
|
||||
/// whether some action against an entity was cancelled through some event.
|
||||
/// This only exists so we have a way to check if an event was cancelled through
|
||||
/// the new api, which also needs to take effect in the legacy api.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
[Obsolete("Anytime there's a cancellable method it needs to return an Attempt so we know the outcome instead of this hack, not all services have been updated to use this though yet.")]
|
||||
internal bool WasCancelled
|
||||
{
|
||||
get { return _wasCancelled; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _wasCancelled, Ps.Value.WasCancelledSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Modified Date
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public DateTime UpdateDate
|
||||
{
|
||||
get { return _updateDate; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _updateDate, Ps.Value.UpdateDateSelector); }
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
|
||||
internal virtual void ResetIdentity()
|
||||
{
|
||||
_hasIdentity = false;
|
||||
_id = default(int);
|
||||
_key = Guid.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call on entity saved when first added
|
||||
/// </summary>
|
||||
internal virtual void AddingEntity()
|
||||
{
|
||||
if (IsPropertyDirty("CreateDate") == false || _createDate == default(DateTime))
|
||||
CreateDate = DateTime.Now;
|
||||
if (IsPropertyDirty("UpdateDate") == false || _updateDate == default(DateTime))
|
||||
UpdateDate = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call on entity saved/updated
|
||||
/// </summary>
|
||||
internal virtual void UpdatingEntity()
|
||||
{
|
||||
if (IsPropertyDirty("UpdateDate") == false || _updateDate == default(DateTime))
|
||||
UpdateDate = DateTime.Now;
|
||||
|
||||
//this is just in case
|
||||
if (_createDate == default(DateTime))
|
||||
CreateDate = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current entity has an identity, eg. Id.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual bool HasIdentity
|
||||
{
|
||||
get
|
||||
{
|
||||
return _hasIdentity;
|
||||
}
|
||||
protected set { SetPropertyValueAndDetectChanges(value, ref _hasIdentity, Ps.Value.HasIdentitySelector); }
|
||||
}
|
||||
|
||||
//TODO: Make this NOT virtual or even exist really!
|
||||
public virtual bool SameIdentityAs(IEntity other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
return false;
|
||||
if (ReferenceEquals(this, other))
|
||||
return true;
|
||||
|
||||
return SameIdentityAs(other as Entity);
|
||||
}
|
||||
|
||||
public virtual bool Equals(Entity other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
return false;
|
||||
if (ReferenceEquals(this, other))
|
||||
return true;
|
||||
|
||||
return SameIdentityAs(other);
|
||||
}
|
||||
|
||||
//TODO: Make this NOT virtual or even exist really!
|
||||
public virtual Type GetRealType()
|
||||
{
|
||||
return GetType();
|
||||
}
|
||||
|
||||
//TODO: Make this NOT virtual or even exist really!
|
||||
public virtual bool SameIdentityAs(Entity other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
return false;
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
return true;
|
||||
|
||||
if (GetType() == other.GetRealType() && HasIdentity && other.HasIdentity)
|
||||
return other.Id.Equals(Id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
return false;
|
||||
if (ReferenceEquals(this, obj))
|
||||
return true;
|
||||
|
||||
return SameIdentityAs(obj as IEntity);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = HasIdentity.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Id;
|
||||
hashCode = (hashCode * 397) ^ GetType().GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual object DeepClone()
|
||||
{
|
||||
//Memberwise clone on Entity will work since it doesn't have any deep elements
|
||||
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
|
||||
var ignored = this.Key; // ensure that 'this' has a key, before cloning
|
||||
var clone = (Entity)MemberwiseClone();
|
||||
//ensure the clone has it's own dictionaries
|
||||
clone.ResetChangeTrackingCollections();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//Automatically deep clone ref properties that are IDeepCloneable
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
public class EntityPath
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for aggregate roots
|
||||
/// </summary>
|
||||
public interface IAggregateRoot : IDeletableEntity
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that defines the object is tracking property changes and if it is Dirty
|
||||
/// </summary>
|
||||
public interface ICanBeDirty
|
||||
{
|
||||
bool IsDirty();
|
||||
bool IsPropertyDirty(string propName);
|
||||
void ResetDirtyProperties();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
public interface IDeletableEntity : IEntity
|
||||
{
|
||||
[DataMember]
|
||||
DateTime? DeletedDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an Entity.
|
||||
/// Entities should always have an Id, Created and Modified date
|
||||
/// </summary>
|
||||
/// <remarks>The current database schema doesn't provide a modified date
|
||||
/// for all entities, so this will have to be changed at a later stage.</remarks>
|
||||
public interface IEntity : IDeepCloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// The Id of the entity
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Guid based Id
|
||||
/// </summary>
|
||||
/// <remarks>The key is currectly used to store the Unique Id from the
|
||||
/// umbracoNode table, which many of the entities are based on.</remarks>
|
||||
[DataMember]
|
||||
Guid Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Created Date
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
DateTime CreateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Modified Date
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
DateTime UpdateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current entity has an identity, eg. Id.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
bool HasIdentity { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that defines if the object is tracking property changes and that is is also
|
||||
/// remembering what property changes had been made after the changes were committed.
|
||||
/// </summary>
|
||||
public interface IRememberBeingDirty : ICanBeDirty
|
||||
{
|
||||
bool WasDirty();
|
||||
bool WasPropertyDirty(string propertyName);
|
||||
void ForgetPreviouslyDirtyProperties();
|
||||
void ResetDirtyProperties(bool rememberPreviouslyChangedProperties);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
public interface IUmbracoEntity : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Profile of the user who created this Entity
|
||||
/// </summary>
|
||||
int CreatorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the level of the Entity
|
||||
/// </summary>
|
||||
int Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Name of the Entity
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Id of the Parent Entity
|
||||
/// </summary>
|
||||
int ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the Entity
|
||||
/// </summary>
|
||||
string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order of the Entity
|
||||
/// </summary>
|
||||
int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether this Entity is Trashed or not.
|
||||
/// If an Entity is Trashed it will be located in the Recyclebin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When content is trashed it should be unpublished.
|
||||
/// Not all entities support being trashed, they'll always return false.
|
||||
/// </remarks>
|
||||
bool Trashed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
IDictionary<string, object> AdditionalData { get; }
|
||||
}
|
||||
}
|
||||
+11
-11
@@ -1,11 +1,11 @@
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for value object, eg. objects without
|
||||
/// the same kind of identity as an Entity (with its Id).
|
||||
/// </summary>
|
||||
public interface IValueObject
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for value object, eg. objects without
|
||||
/// the same kind of identity as an Entity (with its Id).
|
||||
/// </summary>
|
||||
public interface IValueObject
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class for use to implement IRememberBeingDirty/ICanBeDirty
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class TracksChangesEntityBase : IRememberBeingDirty
|
||||
{
|
||||
//TODO: This needs to go on to ICanBeDirty http://issues.umbraco.org/issue/U4-5662
|
||||
public virtual IEnumerable<string> GetDirtyProperties()
|
||||
{
|
||||
return _propertyChangedInfo == null
|
||||
? Enumerable.Empty<string>()
|
||||
: _propertyChangedInfo.Where(x => x.Value).Select(x => x.Key);
|
||||
}
|
||||
|
||||
internal virtual IEnumerable<string> GetPreviouslyDirtyProperties()
|
||||
{
|
||||
return _lastPropertyChangedInfo == null
|
||||
? Enumerable.Empty<string>()
|
||||
: _lastPropertyChangedInfo.Where(x => x.Value).Select(x => x.Key);
|
||||
}
|
||||
|
||||
private bool _changeTrackingEnabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the properties that have changed
|
||||
/// </summary>
|
||||
private IDictionary<string, bool> _propertyChangedInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the properties that we're changed before the last commit (or last call to ResetDirtyProperties)
|
||||
/// </summary>
|
||||
private IDictionary<string, bool> _lastPropertyChangedInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Property changed event
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Method to call on a property setter.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">The property info.</param>
|
||||
protected virtual void OnPropertyChanged(PropertyInfo propertyInfo)
|
||||
{
|
||||
//return if we're not tracking changes
|
||||
if (_changeTrackingEnabled == false) return;
|
||||
|
||||
if (_propertyChangedInfo == null)
|
||||
_propertyChangedInfo = new Dictionary<string, bool>();
|
||||
|
||||
_propertyChangedInfo[propertyInfo.Name] = true;
|
||||
|
||||
if (PropertyChanged != null)
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyInfo.Name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a specific property on the current entity is dirty.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property to check</param>
|
||||
/// <returns>True if Property is dirty, otherwise False</returns>
|
||||
public virtual bool IsPropertyDirty(string propertyName)
|
||||
{
|
||||
return _propertyChangedInfo != null && _propertyChangedInfo.Any(x => x.Key == propertyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current entity is dirty.
|
||||
/// </summary>
|
||||
/// <returns>True if entity is dirty, otherwise False</returns>
|
||||
public virtual bool IsDirty()
|
||||
{
|
||||
return _propertyChangedInfo != null && _propertyChangedInfo.Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the entity had been changed and the changes were committed
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual bool WasDirty()
|
||||
{
|
||||
return _lastPropertyChangedInfo != null && _lastPropertyChangedInfo.Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a specific property on the current entity was changed and the changes were committed
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property to check</param>
|
||||
/// <returns>True if Property was changed, otherwise False. Returns false if the entity had not been previously changed.</returns>
|
||||
public virtual bool WasPropertyDirty(string propertyName)
|
||||
{
|
||||
return _lastPropertyChangedInfo != null && _lastPropertyChangedInfo.Any(x => x.Key == propertyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the remembered dirty properties from before the last commit
|
||||
/// </summary>
|
||||
public void ForgetPreviouslyDirtyProperties()
|
||||
{
|
||||
//NOTE: We cannot .Clear() because when we memberwise clone this will be the SAME
|
||||
// instance as the one on the clone, so we need to create a new instance.
|
||||
_lastPropertyChangedInfo = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets dirty properties by clearing the dictionary used to track changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Please note that resetting the dirty properties could potentially
|
||||
/// obstruct the saving of a new or updated entity.
|
||||
/// </remarks>
|
||||
public virtual void ResetDirtyProperties()
|
||||
{
|
||||
ResetDirtyProperties(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets dirty properties by clearing the dictionary used to track changes.
|
||||
/// </summary>
|
||||
/// <param name="rememberPreviouslyChangedProperties">
|
||||
/// true if we are to remember the last changes made after resetting
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// Please note that resetting the dirty properties could potentially
|
||||
/// obstruct the saving of a new or updated entity.
|
||||
/// </remarks>
|
||||
public virtual void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
if (rememberPreviouslyChangedProperties)
|
||||
{
|
||||
//copy the changed properties to the last changed properties
|
||||
if (_propertyChangedInfo != null)
|
||||
{
|
||||
_lastPropertyChangedInfo = _propertyChangedInfo.ToDictionary(v => v.Key, v => v.Value);
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: We cannot .Clear() because when we memberwise clone this will be the SAME
|
||||
// instance as the one on the clone, so we need to create a new instance.
|
||||
_propertyChangedInfo = null;
|
||||
}
|
||||
|
||||
public void ResetChangeTrackingCollections()
|
||||
{
|
||||
_propertyChangedInfo = null;
|
||||
_lastPropertyChangedInfo = null;
|
||||
}
|
||||
|
||||
public void DisableChangeTracking()
|
||||
{
|
||||
_changeTrackingEnabled = false;
|
||||
}
|
||||
|
||||
public void EnableChangeTracking()
|
||||
{
|
||||
_changeTrackingEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by inheritors to set the value of properties, this will detect if the property value actually changed and if it did
|
||||
/// it will ensure that the property has a dirty flag set.
|
||||
/// </summary>
|
||||
/// <param name="newVal"></param>
|
||||
/// <param name="origVal"></param>
|
||||
/// <param name="propertySelector"></param>
|
||||
/// <returns>returns true if the value changed</returns>
|
||||
/// <remarks>
|
||||
/// This is required because we don't want a property to show up as "dirty" if the value is the same. For example, when we
|
||||
/// save a document type, nearly all properties are flagged as dirty just because we've 'reset' them, but they are all set
|
||||
/// to the same value, so it's really not dirty.
|
||||
/// </remarks>
|
||||
internal void SetPropertyValueAndDetectChanges<T>(T newVal, ref T origVal, PropertyInfo propertySelector)
|
||||
{
|
||||
if ((typeof(T) == typeof(string) == false) && TypeHelper.IsTypeAssignableFrom<IEnumerable>(typeof(T)))
|
||||
{
|
||||
throw new InvalidOperationException("This method does not support IEnumerable instances. For IEnumerable instances a manual custom equality check will be required");
|
||||
}
|
||||
|
||||
SetPropertyValueAndDetectChanges(newVal, ref origVal, propertySelector, EqualityComparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by inheritors to set the value of properties, this will detect if the property value actually changed and if it did
|
||||
/// it will ensure that the property has a dirty flag set.
|
||||
/// </summary>
|
||||
/// <param name="newVal"></param>
|
||||
/// <param name="origVal"></param>
|
||||
/// <param name="propertySelector"></param>
|
||||
/// <param name="comparer">The equality comparer to use</param>
|
||||
/// <returns>returns true if the value changed</returns>
|
||||
/// <remarks>
|
||||
/// This is required because we don't want a property to show up as "dirty" if the value is the same. For example, when we
|
||||
/// save a document type, nearly all properties are flagged as dirty just because we've 'reset' them, but they are all set
|
||||
/// to the same value, so it's really not dirty.
|
||||
/// </remarks>
|
||||
internal void SetPropertyValueAndDetectChanges<T>(T newVal, ref T origVal, PropertyInfo propertySelector, IEqualityComparer<T> comparer)
|
||||
{
|
||||
//don't track changes, just set the value
|
||||
if (_changeTrackingEnabled == false)
|
||||
{
|
||||
//set the original value
|
||||
origVal = newVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
//check changed
|
||||
var changed = comparer.Equals(origVal, newVal) == false;
|
||||
|
||||
//set the original value
|
||||
origVal = newVal;
|
||||
|
||||
//raise the event if it was changed
|
||||
if (changed)
|
||||
{
|
||||
OnPropertyChanged(propertySelector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a folder for organizing entities such as content types and data types.
|
||||
/// </summary>
|
||||
public sealed class EntityContainer : TreeEntityBase, IUmbracoEntity
|
||||
public sealed class EntityContainer : UmbracoEntity
|
||||
{
|
||||
private readonly Guid _containedObjectType;
|
||||
|
||||
private static readonly Dictionary<Guid, Guid> ObjectTypeMap = new Dictionary<Guid, Guid>
|
||||
{
|
||||
{ Constants.ObjectTypes.DataType, Constants.ObjectTypes.DataTypeContainer },
|
||||
{ Constants.ObjectTypes.DocumentType, Constants.ObjectTypes.DocumentTypeContainer },
|
||||
{ Constants.ObjectTypes.MediaType, Constants.ObjectTypes.MediaTypeContainer }
|
||||
{ Constants.ObjectTypes.DataTypeGuid, Constants.ObjectTypes.DataTypeContainerGuid },
|
||||
{ Constants.ObjectTypes.DocumentTypeGuid, Constants.ObjectTypes.DocumentTypeContainerGuid },
|
||||
{ Constants.ObjectTypes.MediaTypeGuid, Constants.ObjectTypes.MediaTypeContainerGuid }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -27,7 +24,7 @@ namespace Umbraco.Core.Models
|
||||
public EntityContainer(Guid containedObjectType)
|
||||
{
|
||||
if (ObjectTypeMap.ContainsKey(containedObjectType) == false)
|
||||
throw new ArgumentException("Not a contained object type.", nameof(containedObjectType));
|
||||
throw new ArgumentException("Not a contained object type.", "containedObjectType");
|
||||
_containedObjectType = containedObjectType;
|
||||
|
||||
ParentId = -1;
|
||||
@@ -39,7 +36,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Initializes a new instance of an <see cref="EntityContainer"/> class.
|
||||
/// </summary>
|
||||
public EntityContainer(int id, Guid uniqueId, int parentId, string path, int level, int sortOrder, Guid containedObjectType, string name, int userId)
|
||||
internal EntityContainer(int id, Guid uniqueId, int parentId, string path, int level, int sortOrder, Guid containedObjectType, string name, int userId)
|
||||
: this(containedObjectType)
|
||||
{
|
||||
Id = id;
|
||||
@@ -55,12 +52,18 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Gets or sets the node object type of the contained objects.
|
||||
/// </summary>
|
||||
public Guid ContainedObjectType => _containedObjectType;
|
||||
public Guid ContainedObjectType
|
||||
{
|
||||
get { return _containedObjectType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the node object type of the container objects.
|
||||
/// </summary>
|
||||
public Guid ContainerObjectType => ObjectTypeMap[_containedObjectType];
|
||||
public Guid ContainerObjectType
|
||||
{
|
||||
get { return ObjectTypeMap[_containedObjectType]; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the container object type corresponding to a contained object type.
|
||||
@@ -70,7 +73,7 @@ namespace Umbraco.Core.Models
|
||||
public static Guid GetContainerObjectType(Guid containedObjectType)
|
||||
{
|
||||
if (ObjectTypeMap.ContainsKey(containedObjectType) == false)
|
||||
throw new ArgumentException("Not a contained object type.", nameof(containedObjectType));
|
||||
throw new ArgumentException("Not a contained object type.", "containedObjectType");
|
||||
return ObjectTypeMap[containedObjectType];
|
||||
}
|
||||
|
||||
@@ -83,8 +86,8 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
var contained = ObjectTypeMap.FirstOrDefault(x => x.Value == containerObjectType).Key;
|
||||
if (contained == null)
|
||||
throw new ArgumentException("Not a container object type.", nameof(containerObjectType));
|
||||
throw new ArgumentException("Not a container object type.", "containerObjectType");
|
||||
return contained;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,24 @@
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public static class EntityExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets additional data.
|
||||
/// Returns true if this entity has just been created and persisted to the data store
|
||||
/// </summary>
|
||||
public static object GetAdditionalDataValueIgnoreCase(this IHaveAdditionalData entity, string key, object defaultValue)
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is useful when handling events to determine if an entity is a brand new entity or was
|
||||
/// already existing.
|
||||
/// </remarks>
|
||||
public static bool IsNewEntity(this IEntity entity)
|
||||
{
|
||||
if (!entity.HasAdditionalData) return defaultValue;
|
||||
if (entity.AdditionalData.ContainsKeyIgnoreCase(key) == false) return defaultValue;
|
||||
return entity.AdditionalData.GetValueIgnoreCase(key, defaultValue);
|
||||
var dirty = (IRememberBeingDirty)entity;
|
||||
return dirty.WasPropertyDirty("Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+187
-172
@@ -1,172 +1,187 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an abstract file which provides basic functionality for a File with an Alias and Name
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class File : EntityBase, IFile
|
||||
{
|
||||
private string _path;
|
||||
private string _originalPath;
|
||||
|
||||
// initialize to string.Empty so that it is possible to save a new file,
|
||||
// should use the lazyContent ctor to set it to null when loading existing.
|
||||
// cannot simply use HasIdentity as some classes (eg Script) override it
|
||||
// in a weird way.
|
||||
private string _content;
|
||||
internal Func<File, string> GetFileContent { get; set; }
|
||||
|
||||
protected File(string path, Func<File, string> getFileContent = null)
|
||||
{
|
||||
_path = SanitizePath(path);
|
||||
_originalPath = _path;
|
||||
GetFileContent = getFileContent;
|
||||
_content = getFileContent != null ? null : string.Empty;
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo<File, string>(x => x.Content);
|
||||
public readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo<File, string>(x => x.Path);
|
||||
}
|
||||
|
||||
private string _alias;
|
||||
private string _name;
|
||||
|
||||
private static string SanitizePath(string path)
|
||||
{
|
||||
return path
|
||||
.Replace('\\', System.IO.Path.DirectorySeparatorChar)
|
||||
.Replace('/', System.IO.Path.DirectorySeparatorChar);
|
||||
|
||||
//Don't strip the start - this was a bug fixed in 7.3, see ScriptRepositoryTests.PathTests
|
||||
//.TrimStart(System.IO.Path.DirectorySeparatorChar)
|
||||
//.TrimStart('/');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the File including extension
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual string Name
|
||||
{
|
||||
get { return _name ?? (_name = System.IO.Path.GetFileName(Path)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Alias of the File, which is the name without the extension
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual string Alias
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_alias == null)
|
||||
{
|
||||
var name = System.IO.Path.GetFileName(Path);
|
||||
if (name == null) return string.Empty;
|
||||
var lastIndexOf = name.LastIndexOf(".", StringComparison.InvariantCultureIgnoreCase);
|
||||
_alias = name.Substring(0, lastIndexOf);
|
||||
}
|
||||
return _alias;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path to the File from the root of the file's associated IFileSystem
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual string Path
|
||||
{
|
||||
get { return _path; }
|
||||
set
|
||||
{
|
||||
//reset
|
||||
_alias = null;
|
||||
_name = null;
|
||||
|
||||
SetPropertyValueAndDetectChanges(SanitizePath(value), ref _path, Ps.Value.PathSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original path of the file
|
||||
/// </summary>
|
||||
public string OriginalPath
|
||||
{
|
||||
get { return _originalPath; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to re-set the OriginalPath to the Path
|
||||
/// </summary>
|
||||
public void ResetOriginalPath()
|
||||
{
|
||||
_originalPath = _path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Content of a File
|
||||
/// </summary>
|
||||
/// <remarks>Marked as DoNotClone, because it should be lazy-reloaded from disk.</remarks>
|
||||
[DataMember]
|
||||
[DoNotClone]
|
||||
public virtual string Content
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_content != null)
|
||||
return _content;
|
||||
|
||||
// else, must lazy-load, and ensure it's not null
|
||||
if (GetFileContent != null)
|
||||
_content = GetFileContent(this);
|
||||
return _content ?? (_content = string.Empty);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(
|
||||
value ?? string.Empty, // cannot set to null
|
||||
ref _content, Ps.Value.ContentSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website)
|
||||
/// </summary>
|
||||
public string VirtualPath { get; set; }
|
||||
|
||||
// this exists so that class that manage name and alias differently, eg Template,
|
||||
// can implement their own cloning - (though really, not sure it's even needed)
|
||||
protected virtual void DeepCloneNameAndAlias(File clone)
|
||||
{
|
||||
// set fields that have a lazy value, by forcing evaluation of the lazy
|
||||
clone._name = Name;
|
||||
clone._alias = Alias;
|
||||
}
|
||||
|
||||
protected override void PerformDeepClone(object clone)
|
||||
{
|
||||
base.PerformDeepClone(clone);
|
||||
|
||||
var clonedFile = (File)clone;
|
||||
|
||||
// clear fields that were memberwise-cloned and that we don't want to clone
|
||||
clonedFile._content = null;
|
||||
|
||||
// ...
|
||||
DeepCloneNameAndAlias(clonedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an abstract file which provides basic functionality for a File with an Alias and Name
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class File : Entity, IFile
|
||||
{
|
||||
private string _path;
|
||||
private string _originalPath;
|
||||
|
||||
// initialize to string.Empty so that it is possible to save a new file,
|
||||
// should use the lazyContent ctor to set it to null when loading existing.
|
||||
// cannot simply use HasIdentity as some classes (eg Script) override it
|
||||
// in a weird way.
|
||||
private string _content;
|
||||
internal Func<File, string> GetFileContent { get; set; }
|
||||
|
||||
protected File(string path, Func<File, string> getFileContent = null)
|
||||
{
|
||||
_path = SanitizePath(path);
|
||||
_originalPath = _path;
|
||||
GetFileContent = getFileContent;
|
||||
_content = getFileContent != null ? null : string.Empty;
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo<File, string>(x => x.Content);
|
||||
public readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo<File, string>(x => x.Path);
|
||||
}
|
||||
|
||||
private string _alias;
|
||||
private string _name;
|
||||
|
||||
private static string SanitizePath(string path)
|
||||
{
|
||||
return path
|
||||
.Replace('\\', System.IO.Path.DirectorySeparatorChar)
|
||||
.Replace('/', System.IO.Path.DirectorySeparatorChar);
|
||||
|
||||
//Don't strip the start - this was a bug fixed in 7.3, see ScriptRepositoryTests.PathTests
|
||||
//.TrimStart(System.IO.Path.DirectorySeparatorChar)
|
||||
//.TrimStart('/');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the File including extension
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual string Name
|
||||
{
|
||||
get { return _name ?? (_name = System.IO.Path.GetFileName(Path)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Alias of the File, which is the name without the extension
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual string Alias
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_alias == null)
|
||||
{
|
||||
var name = System.IO.Path.GetFileName(Path);
|
||||
if (name == null) return string.Empty;
|
||||
var lastIndexOf = name.LastIndexOf(".", StringComparison.InvariantCultureIgnoreCase);
|
||||
_alias = name.Substring(0, lastIndexOf);
|
||||
}
|
||||
return _alias;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path to the File from the root of the file's associated IFileSystem
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual string Path
|
||||
{
|
||||
get { return _path; }
|
||||
set
|
||||
{
|
||||
//reset
|
||||
_alias = null;
|
||||
_name = null;
|
||||
|
||||
SetPropertyValueAndDetectChanges(SanitizePath(value), ref _path, Ps.Value.PathSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original path of the file
|
||||
/// </summary>
|
||||
public string OriginalPath
|
||||
{
|
||||
get { return _originalPath; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to re-set the OriginalPath to the Path
|
||||
/// </summary>
|
||||
public void ResetOriginalPath()
|
||||
{
|
||||
_originalPath = _path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Content of a File
|
||||
/// </summary>
|
||||
/// <remarks>Marked as DoNotClone, because it should be lazy-reloaded from disk.</remarks>
|
||||
[DataMember]
|
||||
[DoNotClone]
|
||||
public virtual string Content
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_content != null)
|
||||
return _content;
|
||||
|
||||
// else, must lazy-load, and ensure it's not null
|
||||
if (GetFileContent != null)
|
||||
_content = GetFileContent(this);
|
||||
return _content ?? (_content = string.Empty);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(
|
||||
value ?? string.Empty, // cannot set to null
|
||||
ref _content, Ps.Value.ContentSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website)
|
||||
/// </summary>
|
||||
public string VirtualPath { get; set; }
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in future versions")]
|
||||
public virtual bool IsValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// this exists so that class that manage name and alias differently, eg Template,
|
||||
// can implement their own cloning - (though really, not sure it's even needed)
|
||||
protected virtual void DeepCloneNameAndAlias(File clone)
|
||||
{
|
||||
// set fields that have a lazy value, by forcing evaluation of the lazy
|
||||
clone._name = Name;
|
||||
clone._alias = Alias;
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (File) base.DeepClone();
|
||||
|
||||
// clear fields that were memberwise-cloned and that we don't want to clone
|
||||
clone._content = null;
|
||||
|
||||
// turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
|
||||
// ...
|
||||
DeepCloneNameAndAlias(clone);
|
||||
|
||||
// this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
// re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
internal sealed class Folder : EntityBase
|
||||
internal sealed class Folder : Entity
|
||||
{
|
||||
public Folder(string folderPath)
|
||||
{
|
||||
@@ -11,4 +11,4 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -82,4 +82,4 @@ namespace Umbraco.Core.Models
|
||||
public string View { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Models
|
||||
/// <para>Depending on audit loggers, these properties can be purely free-form text, or
|
||||
/// contain json serialized objects.</para>
|
||||
/// </remarks>
|
||||
public interface IAuditEntry : IEntity, IRememberBeingDirty
|
||||
public interface IAuditEntry : IAggregateRoot, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the user triggering the audited event.
|
||||
|
||||
@@ -1,35 +1,12 @@
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using System;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an audit item.
|
||||
/// </summary>
|
||||
public interface IAuditItem : IEntity
|
||||
public interface IAuditItem : IAggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the audit type.
|
||||
/// </summary>
|
||||
AuditType AuditType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audited entity type.
|
||||
/// </summary>
|
||||
string EntityType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audit user identifier.
|
||||
/// </summary>
|
||||
int UserId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audit comments.
|
||||
/// </summary>
|
||||
string Comment { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets optional additional data parameters.
|
||||
/// </summary>
|
||||
string Parameters { get; }
|
||||
AuditType AuditType { get; }
|
||||
int UserId { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models
|
||||
/// example, an application), and an action (whatever is consented).</para>
|
||||
/// <para>A consent state registers the state of the consent (granted, revoked...).</para>
|
||||
/// </remarks>
|
||||
public interface IConsent : IEntity, IRememberBeingDirty
|
||||
public interface IConsent : IAggregateRoot, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the consent entity represents the current state.
|
||||
|
||||
@@ -1,181 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a document.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>A document can be published, rendered by a template.</para>
|
||||
/// </remarks>
|
||||
public interface IContent : IContentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the content schedule
|
||||
/// </summary>
|
||||
ContentScheduleCollection ContentSchedule { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template used to render the content.
|
||||
/// </summary>
|
||||
ITemplate Template { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is published.
|
||||
/// </summary>
|
||||
bool Published { get; }
|
||||
|
||||
PublishedState PublishedState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content has been edited.
|
||||
/// </summary>
|
||||
bool Edited { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the published version identifier.
|
||||
/// </summary>
|
||||
int PublishedVersionId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content item is a blueprint.
|
||||
/// </summary>
|
||||
bool Blueprint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the template used to render the published version of the content.
|
||||
/// </summary>
|
||||
/// <remarks>When editing the content, the template can change, but this will not until the content is published.</remarks>
|
||||
ITemplate PublishTemplate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the published version of the content.
|
||||
/// </summary>
|
||||
/// <remarks>When editing the content, the name can change, but this will not until the content is published.</remarks>
|
||||
string PublishName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier of the user who published the content.
|
||||
/// </summary>
|
||||
int? PublisherId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the date and time the content was published.
|
||||
/// </summary>
|
||||
DateTime? PublishDate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type of this content.
|
||||
/// </summary>
|
||||
IContentType ContentType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a culture is published.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>A culture becomes published whenever values for this culture are published,
|
||||
/// and the content published name for this culture is non-null. It becomes non-published
|
||||
/// whenever values for this culture are unpublished.</para>
|
||||
/// <para>A culture becomes published as soon as PublishCulture has been invoked,
|
||||
/// even though the document might now have been saved yet (and can have no identity).</para>
|
||||
/// <para>Does not support the '*' wildcard (returns false).</para>
|
||||
/// </remarks>
|
||||
bool IsCulturePublished(string culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a culture was published.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Mirrors <see cref="IsCulturePublished"/> whenever the content item is saved.</para>
|
||||
/// </remarks>
|
||||
bool WasCulturePublished(string culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the date a culture was published.
|
||||
/// </summary>
|
||||
DateTime? GetPublishDate(string culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicated whether a given culture is edited.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>A culture is edited when it is available, and not published or published but
|
||||
/// with changes.</para>
|
||||
/// <para>A culture can be edited even though the document might now have been saved yet (and can have no identity).</para>
|
||||
/// <para>Does not support the '*' wildcard (returns false).</para>
|
||||
/// </remarks>
|
||||
bool IsCultureEdited(string culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the published version of the content for a given culture.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When editing the content, the name can change, but this will not until the content is published.</para>
|
||||
/// <para>When <paramref name="culture"/> is <c>null</c>, gets the invariant
|
||||
/// language, which is the value of the <see cref="PublishName"/> property.</para>
|
||||
/// </remarks>
|
||||
string GetPublishName(string culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the published culture infos of the content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Because a dictionary key cannot be <c>null</c> this cannot get the invariant
|
||||
/// name, which must be get via the <see cref="PublishName"/> property.</para>
|
||||
/// </remarks>
|
||||
IReadOnlyDictionary<string, ContentCultureInfos> PublishCultureInfos { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the published cultures.
|
||||
/// </summary>
|
||||
IEnumerable<string> PublishedCultures { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the edited cultures.
|
||||
/// </summary>
|
||||
IEnumerable<string> EditedCultures { get; }
|
||||
|
||||
// fixme - these two should move to some kind of service
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="IContentType"/> for the current content object
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <remarks>Leaves PropertyTypes intact after change</remarks>
|
||||
void ChangeContentType(IContentType contentType);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="IContentType"/> for the current content object and removes PropertyTypes,
|
||||
/// which are not part of the new ContentType.
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <param name="clearProperties">Boolean indicating whether to clear PropertyTypes upon change</param>
|
||||
void ChangeContentType(IContentType contentType, bool clearProperties);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IContent DeepCloneWithResetIdentities();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a culture to be published.
|
||||
/// </summary>
|
||||
/// <returns>A value indicating whether the culture can be published.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Fails if properties don't pass variant validation rules.</para>
|
||||
/// <para>Publishing must be finalized via the content service SavePublishing method.</para>
|
||||
/// </remarks>
|
||||
bool PublishCulture(string culture = "*");
|
||||
|
||||
/// <summary>
|
||||
/// Registers a culture to be unpublished.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Unpublishing must be finalized via the content service SavePublishing method.</para>
|
||||
/// </remarks>
|
||||
void UnpublishCulture(string culture = "*");
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Content object
|
||||
/// </summary>
|
||||
public interface IContent : IContentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the template used by the Content.
|
||||
/// This is used to override the default one from the ContentType.
|
||||
/// </summary>
|
||||
ITemplate Template { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether the Content is Published or not
|
||||
/// </summary>
|
||||
bool Published { get; }
|
||||
|
||||
[Obsolete("This will be removed in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
string Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the date the Content should be released and thus be published
|
||||
/// </summary>
|
||||
DateTime? ReleaseDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the date the Content should expire and thus be unpublished
|
||||
/// </summary>
|
||||
DateTime? ExpireDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id of the user who wrote/updated the Content
|
||||
/// </summary>
|
||||
int WriterId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this content object
|
||||
/// </summary>
|
||||
IContentType ContentType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current status of the Content
|
||||
/// </summary>
|
||||
ContentStatus Status { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="IContentType"/> for the current content object
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <remarks>Leaves PropertyTypes intact after change</remarks>
|
||||
void ChangeContentType(IContentType contentType);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="IContentType"/> for the current content object and removes PropertyTypes,
|
||||
/// which are not part of the new ContentType.
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <param name="clearProperties">Boolean indicating whether to clear PropertyTypes upon change</param>
|
||||
void ChangeContentType(IContentType contentType, bool clearProperties);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Published state of the content object
|
||||
/// </summary>
|
||||
void ChangePublishedState(PublishedState state);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IContent DeepCloneWithResetIdentities();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content has a published version.
|
||||
/// </summary>
|
||||
bool HasPublishedVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the published version, if any.
|
||||
/// </summary>
|
||||
Guid PublishedVersionGuid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content item is a blueprint.
|
||||
/// </summary>
|
||||
bool IsBlueprint { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,149 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for content items.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Content items are documents, medias and members.</para>
|
||||
/// <para>Content items have a content type, and properties.</para>
|
||||
/// </remarks>
|
||||
public interface IContentBase : IUmbracoEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Integer Id of the default ContentType
|
||||
/// </summary>
|
||||
int ContentTypeId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier of the writer.
|
||||
/// </summary>
|
||||
int WriterId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version identifier.
|
||||
/// </summary>
|
||||
int VersionId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name of the content item for a specified culture.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When <paramref name="culture"/> is null, sets the invariant
|
||||
/// culture name, which sets the <see cref="TreeEntityBase.Name"/> property.</para>
|
||||
/// <para>When <paramref name="culture"/> is not null, throws if the content
|
||||
/// type does not vary by culture.</para>
|
||||
/// </remarks>
|
||||
void SetCultureName(string value, string culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the content item for a specified language.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When <paramref name="culture"/> is null, gets the invariant
|
||||
/// culture name, which is the value of the <see cref="TreeEntityBase.Name"/> property.</para>
|
||||
/// <para>When <paramref name="culture"/> is not null, and the content type
|
||||
/// does not vary by culture, returns null.</para>
|
||||
/// </remarks>
|
||||
string GetCultureName(string culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets culture infos of the content item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Because a dictionary key cannot be <c>null</c> this cannot contain the invariant
|
||||
/// culture name, which must be get or set via the <see cref="TreeEntityBase.Name"/> property.</para>
|
||||
/// </remarks>
|
||||
IReadOnlyDictionary<string, ContentCultureInfos> CultureInfos { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available cultures.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Cannot contain the invariant culture, which is always available.</para>
|
||||
/// </remarks>
|
||||
IEnumerable<string> AvailableCultures { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a given culture is available.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>A culture becomes available whenever the content name for this culture is
|
||||
/// non-null, and it becomes unavailable whenever the content name is null.</para>
|
||||
/// <para>Returns <c>false</c> for the invariant culture, in order to be consistent
|
||||
/// with <seealso cref="AvailableCultures"/>, even though the invariant culture is
|
||||
/// always available.</para>
|
||||
/// <para>Does not support the '*' wildcard (returns false).</para>
|
||||
/// </remarks>
|
||||
bool IsCultureAvailable(string culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the date a culture was updated.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When <paramref name="culture" /> is <c>null</c>, returns <c>null</c>.</para>
|
||||
/// <para>If the specified culture is not available, returns <c>null</c>.</para>
|
||||
/// </remarks>
|
||||
DateTime? GetUpdateDate(string culture);
|
||||
|
||||
/// <summary>
|
||||
/// List of properties, which make up all the data available for this Content object
|
||||
/// </summary>
|
||||
/// <remarks>Properties are loaded as part of the Content object graph</remarks>
|
||||
PropertyCollection Properties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of PropertyGroups available on this Content object
|
||||
/// </summary>
|
||||
/// <remarks>PropertyGroups are kind of lazy loaded as part of the object graph</remarks>
|
||||
IEnumerable<PropertyGroup> PropertyGroups { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of PropertyTypes available on this Content object
|
||||
/// </summary>
|
||||
/// <remarks>PropertyTypes are kind of lazy loaded as part of the object graph</remarks>
|
||||
IEnumerable<PropertyType> PropertyTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content entity has a property with the supplied alias.
|
||||
/// </summary>
|
||||
/// <remarks>Indicates that the content entity has a property with the supplied alias, but
|
||||
/// not necessarily that the content has a value for that property. Could be missing.</remarks>
|
||||
bool HasProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a Property
|
||||
/// </summary>
|
||||
/// <remarks>Values 'null' and 'empty' are equivalent for culture and segment.</remarks>
|
||||
object GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the typed value of a Property
|
||||
/// </summary>
|
||||
/// <remarks>Values 'null' and 'empty' are equivalent for culture and segment.</remarks>
|
||||
TValue GetValue<TValue>(string propertyTypeAlias, string culture = null, string segment = null, bool published = false);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the (edited) value of a Property
|
||||
/// </summary>
|
||||
/// <remarks>Values 'null' and 'empty' are equivalent for culture and segment.</remarks>
|
||||
void SetValue(string propertyTypeAlias, object value, string culture = null, string segment = null);
|
||||
|
||||
/// <summary>
|
||||
/// Copies values from another document.
|
||||
/// </summary>
|
||||
void CopyFrom(IContent other, string culture = "*");
|
||||
|
||||
// fixme validate published cultures?
|
||||
|
||||
/// <summary>
|
||||
/// Validates the content item's properties pass variant rules
|
||||
/// </summary>
|
||||
/// <para>If the content type is variant, then culture can be either '*' or an actual culture, but neither 'null' nor
|
||||
/// 'empty'. If the content type is invariant, then culture can be either '*' or null or empty.</para>
|
||||
Property[] ValidateProperties(string culture = "*");
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the base for a Content object with properties that
|
||||
/// are shared between Content and Media.
|
||||
/// </summary>
|
||||
public interface IContentBase : IUmbracoEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Integer Id of the default ContentType
|
||||
/// </summary>
|
||||
int ContentTypeId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Guid Id of the Content's Version
|
||||
/// </summary>
|
||||
Guid Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of properties, which make up all the data available for this Content object
|
||||
/// </summary>
|
||||
/// <remarks>Properties are loaded as part of the Content object graph</remarks>
|
||||
PropertyCollection Properties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of PropertyGroups available on this Content object
|
||||
/// </summary>
|
||||
/// <remarks>PropertyGroups are kind of lazy loaded as part of the object graph</remarks>
|
||||
IEnumerable<PropertyGroup> PropertyGroups { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of PropertyTypes available on this Content object
|
||||
/// </summary>
|
||||
/// <remarks>PropertyTypes are kind of lazy loaded as part of the object graph</remarks>
|
||||
IEnumerable<PropertyType> PropertyTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the content object has a property with the supplied alias
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
|
||||
/// <returns>True if Property with given alias exists, otherwise False</returns>
|
||||
bool HasProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a Property
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
|
||||
/// <returns><see cref="Property"/> Value as an <see cref="object"/></returns>
|
||||
object GetValue(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a Property
|
||||
/// </summary>
|
||||
/// <typeparam name="TPassType">Type of the value to return</typeparam>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
|
||||
/// <returns><see cref="Property"/> Value as a <see cref="TPassType"/></returns>
|
||||
TPassType GetValue<TPassType>(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="System.Object"/> value of a Property
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
|
||||
/// <param name="value">Value to set for the Property</param>
|
||||
void SetValue(string propertyTypeAlias, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether the content and its properties are valid
|
||||
/// </summary>
|
||||
/// <returns>True if content is valid otherwise false</returns>
|
||||
bool IsValid();
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Trashed state of the content object
|
||||
/// </summary>
|
||||
/// <param name="isTrashed">Boolean indicating whether content is trashed (true) or not trashed (false)</param>
|
||||
/// <param name="parentId"> </param>
|
||||
void ChangeTrashedState(bool isTrashed, int parentId = -20);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a ContentType, which Content is based on
|
||||
/// </summary>
|
||||
public interface IContentType : IContentTypeComposition
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default Template of the ContentType
|
||||
/// </summary>
|
||||
ITemplate DefaultTemplate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a list of Templates which are allowed for the ContentType
|
||||
/// </summary>
|
||||
IEnumerable<ITemplate> AllowedTemplates { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if AllowedTemplates contains templateId
|
||||
/// </summary>
|
||||
/// <param name="templateId">The template id to check</param>
|
||||
/// <returns>True if AllowedTemplates contains the templateId else False</returns>
|
||||
bool IsAllowedTemplate(int templateId);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if AllowedTemplates contains templateId
|
||||
/// </summary>
|
||||
/// <param name="templateAlias">The template alias to check</param>
|
||||
/// <returns>True if AllowedTemplates contains the templateAlias else False</returns>
|
||||
bool IsAllowedTemplate(string templateAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default template for the ContentType
|
||||
/// </summary>
|
||||
/// <param name="template">Default <see cref="ITemplate"/></param>
|
||||
void SetDefaultTemplate(ITemplate template);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a template from the list of allowed templates
|
||||
/// </summary>
|
||||
/// <param name="template"><see cref="ITemplate"/> to remove</param>
|
||||
/// <returns>True if template was removed, otherwise False</returns>
|
||||
bool RemoveTemplate(ITemplate template);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <param name="newAlias"></param>
|
||||
/// <returns></returns>
|
||||
IContentType DeepCloneWithResetIdentities(string newAlias);
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a ContentType, which Content is based on
|
||||
/// </summary>
|
||||
public interface IContentType : IContentTypeComposition
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default Template of the ContentType
|
||||
/// </summary>
|
||||
ITemplate DefaultTemplate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a list of Templates which are allowed for the ContentType
|
||||
/// </summary>
|
||||
IEnumerable<ITemplate> AllowedTemplates { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if AllowedTemplates contains templateId
|
||||
/// </summary>
|
||||
/// <param name="templateId">The template id to check</param>
|
||||
/// <returns>True if AllowedTemplates contains the templateId else False</returns>
|
||||
bool IsAllowedTemplate(int templateId);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if AllowedTemplates contains templateId
|
||||
/// </summary>
|
||||
/// <param name="templateAlias">The template alias to check</param>
|
||||
/// <returns>True if AllowedTemplates contains the templateAlias else False</returns>
|
||||
bool IsAllowedTemplate(string templateAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default template for the ContentType
|
||||
/// </summary>
|
||||
/// <param name="template">Default <see cref="ITemplate"/></param>
|
||||
void SetDefaultTemplate(ITemplate template);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a template from the list of allowed templates
|
||||
/// </summary>
|
||||
/// <param name="template"><see cref="ITemplate"/> to remove</param>
|
||||
/// <returns>True if template was removed, otherwise False</returns>
|
||||
bool RemoveTemplate(ITemplate template);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <param name="newAlias"></param>
|
||||
/// <returns></returns>
|
||||
IContentType DeepCloneWithResetIdentities(string newAlias);
|
||||
}
|
||||
}
|
||||
@@ -1,153 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the base for a ContentType with properties that
|
||||
/// are shared between ContentTypes and MediaTypes.
|
||||
/// </summary>
|
||||
public interface IContentTypeBase : IUmbracoEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or Sets the Alias of the ContentType
|
||||
/// </summary>
|
||||
string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Description for the ContentType
|
||||
/// </summary>
|
||||
string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon for the content type. The value is a CSS class name representing
|
||||
/// the icon (eg. <c>icon-home</c>) along with an optional CSS class name representing the
|
||||
/// color (eg. <c>icon-blue</c>). Put together, the value for this scenario would be
|
||||
/// <c>icon-home color-blue</c>.
|
||||
///
|
||||
/// If a class name for the color isn't specified, the icon color will default to black.
|
||||
/// </summary>
|
||||
string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Thumbnail for the ContentType
|
||||
/// </summary>
|
||||
string Thumbnail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a boolean indicating whether this ContentType is allowed at the root
|
||||
/// </summary>
|
||||
bool AllowedAsRoot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a boolean indicating whether this ContentType is a Container
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ContentType Containers doesn't show children in the tree, but rather in grid-type view.
|
||||
/// </remarks>
|
||||
bool IsContainer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content variation of the content type.
|
||||
/// </summary>
|
||||
ContentVariation Variations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Validates that a combination of culture and segment is valid for the content type.
|
||||
/// </summary>
|
||||
/// <param name="culture">The culture.</param>
|
||||
/// <param name="segment">The segment.</param>
|
||||
/// <param name="wildcards">A value indicating whether wilcards are supported.</param>
|
||||
/// <returns>True if the combination is valid; otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The combination must match the content type variation exactly. For instance, if the content type varies by culture,
|
||||
/// then an invariant culture would be invalid.</para>
|
||||
/// </remarks>
|
||||
bool SupportsVariation(string culture, string segment, bool wildcards = false);
|
||||
|
||||
/// <summary>
|
||||
/// Validates that a combination of culture and segment is valid for the content type properties.
|
||||
/// </summary>
|
||||
/// <param name="culture">The culture.</param>
|
||||
/// <param name="segment">The segment.</param>
|
||||
/// <param name="wildcards">A value indicating whether wilcards are supported.</param>
|
||||
/// <returns>True if the combination is valid; otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The combination must be valid for properties of the content type. For instance, if the content type varies by culture,
|
||||
/// then an invariant culture is valid, because some properties may be invariant. On the other hand, if the content type is invariant,
|
||||
/// then a variant culture is invalid, because no property could possibly vary by culture.</para>
|
||||
/// </remarks>
|
||||
bool SupportsPropertyVariation(string culture, string segment, bool wildcards = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a list of integer Ids of the ContentTypes allowed under the ContentType
|
||||
/// </summary>
|
||||
IEnumerable<ContentTypeSort> AllowedContentTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a collection of Property Groups
|
||||
/// </summary>
|
||||
PropertyGroupCollection PropertyGroups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all property types, across all property groups.
|
||||
/// </summary>
|
||||
IEnumerable<PropertyType> PropertyTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the property types that are not in a group.
|
||||
/// </summary>
|
||||
IEnumerable<PropertyType> NoGroupPropertyTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Removes a PropertyType from the current ContentType
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the <see cref="PropertyType"/> to remove</param>
|
||||
void RemovePropertyType(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a PropertyGroup from the current ContentType
|
||||
/// </summary>
|
||||
/// <param name="propertyGroupName">Name of the <see cref="PropertyGroup"/> to remove</param>
|
||||
void RemovePropertyGroup(string propertyGroupName);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a PropertyType with a given alias already exists
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyType with the passed in alias exists, otherwise <c>False</c></returns>
|
||||
bool PropertyTypeExists(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType to a specific PropertyGroup
|
||||
/// </summary>
|
||||
/// <param name="propertyType"><see cref="PropertyType"/> to add</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to add the PropertyType to</param>
|
||||
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
|
||||
bool AddPropertyType(PropertyType propertyType, string propertyGroupName);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType, which does not belong to a PropertyGroup.
|
||||
/// </summary>
|
||||
/// <param name="propertyType"><see cref="PropertyType"/> to add</param>
|
||||
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
|
||||
bool AddPropertyType(PropertyType propertyType);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyGroup.
|
||||
/// This method will also check if a group already exists with the same name and link it to the parent.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the PropertyGroup to add</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyGroup with the passed in name was added, otherwise <c>False</c></returns>
|
||||
bool AddPropertyGroup(string groupName);
|
||||
|
||||
/// <summary>
|
||||
/// Moves a PropertyType to a specified PropertyGroup
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType to move</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to move the PropertyType to</param>
|
||||
/// <returns></returns>
|
||||
bool MovePropertyType(string propertyTypeAlias, string propertyGroupName);
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the base for a ContentType with properties that
|
||||
/// are shared between ContentTypes and MediaTypes.
|
||||
/// </summary>
|
||||
public interface IContentTypeBase : IUmbracoEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or Sets the Alias of the ContentType
|
||||
/// </summary>
|
||||
string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Description for the ContentType
|
||||
/// </summary>
|
||||
string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon for the content type. The value is a CSS class name representing
|
||||
/// the icon (eg. <c>icon-home</c>) along with an optional CSS class name representing the
|
||||
/// color (eg. <c>icon-blue</c>). Put together, the value for this scenario would be
|
||||
/// <c>icon-home color-blue</c>.
|
||||
///
|
||||
/// If a class name for the color isn't specified, the icon color will default to black.
|
||||
/// </summary>
|
||||
string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Thumbnail for the ContentType
|
||||
/// </summary>
|
||||
string Thumbnail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a boolean indicating whether this ContentType is allowed at the root
|
||||
/// </summary>
|
||||
bool AllowedAsRoot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a boolean indicating whether this ContentType is a Container
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ContentType Containers doesn't show children in the tree, but rather in grid-type view.
|
||||
/// </remarks>
|
||||
bool IsContainer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a list of integer Ids of the ContentTypes allowed under the ContentType
|
||||
/// </summary>
|
||||
IEnumerable<ContentTypeSort> AllowedContentTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a collection of Property Groups
|
||||
/// </summary>
|
||||
PropertyGroupCollection PropertyGroups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all property types, across all property groups.
|
||||
/// </summary>
|
||||
IEnumerable<PropertyType> PropertyTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the property types that are not in a group.
|
||||
/// </summary>
|
||||
IEnumerable<PropertyType> NoGroupPropertyTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Removes a PropertyType from the current ContentType
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the <see cref="PropertyType"/> to remove</param>
|
||||
void RemovePropertyType(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a PropertyGroup from the current ContentType
|
||||
/// </summary>
|
||||
/// <param name="propertyGroupName">Name of the <see cref="PropertyGroup"/> to remove</param>
|
||||
void RemovePropertyGroup(string propertyGroupName);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ParentId from the lazy integer id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the Parent</param>
|
||||
void SetLazyParentId(Lazy<int> id);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a PropertyType with a given alias already exists
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyType with the passed in alias exists, otherwise <c>False</c></returns>
|
||||
bool PropertyTypeExists(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType to a specific PropertyGroup
|
||||
/// </summary>
|
||||
/// <param name="propertyType"><see cref="PropertyType"/> to add</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to add the PropertyType to</param>
|
||||
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
|
||||
bool AddPropertyType(PropertyType propertyType, string propertyGroupName);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType, which does not belong to a PropertyGroup.
|
||||
/// </summary>
|
||||
/// <param name="propertyType"><see cref="PropertyType"/> to add</param>
|
||||
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
|
||||
bool AddPropertyType(PropertyType propertyType);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyGroup.
|
||||
/// This method will also check if a group already exists with the same name and link it to the parent.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the PropertyGroup to add</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyGroup with the passed in name was added, otherwise <c>False</c></returns>
|
||||
bool AddPropertyGroup(string groupName);
|
||||
|
||||
/// <summary>
|
||||
/// Moves a PropertyType to a specified PropertyGroup
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType to move</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to move the PropertyType to</param>
|
||||
/// <returns></returns>
|
||||
bool MovePropertyType(string propertyTypeAlias, string propertyGroupName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the Composition of a ContentType
|
||||
/// </summary>
|
||||
public interface IContentTypeComposition : IContentTypeBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the content types that compose this content type.
|
||||
/// </summary>
|
||||
//fixme: we should be storing key references, not the object else we are caching way too much
|
||||
IEnumerable<IContentTypeComposition> ContentTypeComposition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property groups for the entire composition.
|
||||
/// </summary>
|
||||
IEnumerable<PropertyGroup> CompositionPropertyGroups { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property types for the entire composition.
|
||||
/// </summary>
|
||||
IEnumerable<PropertyType> CompositionPropertyTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new ContentType to the list of composite ContentTypes
|
||||
/// </summary>
|
||||
/// <param name="contentType"><see cref="IContentType"/> to add</param>
|
||||
/// <returns>True if ContentType was added, otherwise returns False</returns>
|
||||
bool AddContentType(IContentTypeComposition contentType);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a ContentType with the supplied alias from the the list of composite ContentTypes
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of a <see cref="IContentType"/></param>
|
||||
/// <returns>True if ContentType was removed, otherwise returns False</returns>
|
||||
bool RemoveContentType(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a ContentType with the supplied alias exists in the list of composite ContentTypes
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of a <see cref="IContentType"/></param>
|
||||
/// <returns>True if ContentType with alias exists, otherwise returns False</returns>
|
||||
bool ContentTypeCompositionExists(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of ContentType aliases from the current composition
|
||||
/// </summary>
|
||||
/// <returns>An enumerable list of string aliases</returns>
|
||||
IEnumerable<string> CompositionAliases();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of ContentType Ids from the current composition
|
||||
/// </summary>
|
||||
/// <returns>An enumerable list of integer ids</returns>
|
||||
IEnumerable<int> CompositionIds();
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the Composition of a ContentType
|
||||
/// </summary>
|
||||
public interface IContentTypeComposition : IContentTypeBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the content types that compose this content type.
|
||||
/// </summary>
|
||||
IEnumerable<IContentTypeComposition> ContentTypeComposition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property groups for the entire composition.
|
||||
/// </summary>
|
||||
IEnumerable<PropertyGroup> CompositionPropertyGroups { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property types for the entire composition.
|
||||
/// </summary>
|
||||
IEnumerable<PropertyType> CompositionPropertyTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new ContentType to the list of composite ContentTypes
|
||||
/// </summary>
|
||||
/// <param name="contentType"><see cref="IContentType"/> to add</param>
|
||||
/// <returns>True if ContentType was added, otherwise returns False</returns>
|
||||
bool AddContentType(IContentTypeComposition contentType);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a ContentType with the supplied alias from the the list of composite ContentTypes
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of a <see cref="IContentType"/></param>
|
||||
/// <returns>True if ContentType was removed, otherwise returns False</returns>
|
||||
bool RemoveContentType(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a ContentType with the supplied alias exists in the list of composite ContentTypes
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of a <see cref="IContentType"/></param>
|
||||
/// <returns>True if ContentType with alias exists, otherwise returns False</returns>
|
||||
bool ContentTypeCompositionExists(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of ContentType aliases from the current composition
|
||||
/// </summary>
|
||||
/// <returns>An enumerable list of string aliases</returns>
|
||||
IEnumerable<string> CompositionAliases();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of ContentType Ids from the current composition
|
||||
/// </summary>
|
||||
/// <returns>An enumerable list of integer ids</returns>
|
||||
IEnumerable<int> CompositionIds();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a data type.
|
||||
/// </summary>
|
||||
public interface IDataType : IUmbracoEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the property editor.
|
||||
/// </summary>
|
||||
IDataEditor Editor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property editor alias.
|
||||
/// </summary>
|
||||
string EditorAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the database type for the data type values.
|
||||
/// </summary>
|
||||
/// <remarks>In most cases this is imposed by the property editor, but some editors
|
||||
/// may support storing different types.</remarks>
|
||||
ValueStorageType DatabaseType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the configuration object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The configuration object is serialized to Json and stored into the database.</para>
|
||||
/// <para>The serialized Json is deserialized by the property editor, which by default should
|
||||
/// return a Dictionary{string, object} but could return a typed object e.g. MyEditor.Configuration.</para>
|
||||
/// </remarks>
|
||||
object Configuration { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IDataTypeDefinition : IUmbracoEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// The Property editor alias assigned to the data type
|
||||
/// </summary>
|
||||
string PropertyEditorAlias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id of the DataType control
|
||||
/// </summary>
|
||||
[Obsolete("Property editor's are defined by a string alias from version 7 onwards, use the PropertyEditorAlias property instead")]
|
||||
Guid ControlId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the DatabaseType for which the DataType's value is saved as
|
||||
/// </summary>
|
||||
DataTypeDatabaseType DatabaseType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
namespace Umbraco.Core.Models
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a mean to deep-clone an object.
|
||||
/// </summary>
|
||||
public interface IDeepCloneable
|
||||
{
|
||||
object DeepClone();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IDictionaryItem : IEntity, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or Sets the Parent Id of the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
Guid? ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Key for the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string ItemKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of translations for the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
IEnumerable<IDictionaryTranslation> Translations { get; set; }
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IDictionaryItem : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or Sets the Parent Id of the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
Guid? ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Key for the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string ItemKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of translations for the Dictionary Item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
IEnumerable<IDictionaryTranslation> Translations { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IDictionaryTranslation : IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Language"/> for the translation
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
ILanguage Language { get; set; }
|
||||
|
||||
int LanguageId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the translated text
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Value { get; set; }
|
||||
}
|
||||
}
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IDictionaryTranslation : IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Language"/> for the translation
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
ILanguage Language { get; set; }
|
||||
|
||||
int LanguageId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the translated text
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IDomain : IEntity, IRememberBeingDirty
|
||||
public interface IDomain : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
int? LanguageId { get; set; }
|
||||
string DomainName { get; set; }
|
||||
@@ -14,4 +14,4 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
string LanguageIsoCode { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,50 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a File
|
||||
/// </summary>
|
||||
/// <remarks>Used for Scripts, Stylesheets and Templates</remarks>
|
||||
public interface IFile : IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Name of the File including extension
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Alias of the File, which is the name without the extension
|
||||
/// </summary>
|
||||
string Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path to the File from the root of the file's associated IFileSystem
|
||||
/// </summary>
|
||||
string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original path of the file
|
||||
/// </summary>
|
||||
string OriginalPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Called to re-set the OriginalPath to the Path
|
||||
/// </summary>
|
||||
void ResetOriginalPath();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Content of a File
|
||||
/// </summary>
|
||||
string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website)
|
||||
/// </summary>
|
||||
string VirtualPath { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a File
|
||||
/// </summary>
|
||||
/// <remarks>Used for Scripts, Stylesheets and Templates</remarks>
|
||||
public interface IFile : IAggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Name of the File including extension
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Alias of the File, which is the name without the extension
|
||||
/// </summary>
|
||||
string Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path to the File from the root of the file's associated IFileSystem
|
||||
/// </summary>
|
||||
string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original path of the file
|
||||
/// </summary>
|
||||
string OriginalPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Called to re-set the OriginalPath to the Path
|
||||
/// </summary>
|
||||
void ResetOriginalPath();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Content of a File
|
||||
/// </summary>
|
||||
string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website)
|
||||
/// </summary>
|
||||
string VirtualPath { get; set; }
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in future versions")]
|
||||
bool IsValid();
|
||||
}
|
||||
}
|
||||
@@ -1,57 +1,28 @@
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a language.
|
||||
/// </summary>
|
||||
public interface ILanguage : IEntity, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the ISO code of the language.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string IsoCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the culture name of the language.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string CultureName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CultureInfo"/> object for the language.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
CultureInfo CultureInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the language is the default language.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool IsDefault { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the language is mandatory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When a language is mandatory, a multi-lingual document cannot be published
|
||||
/// without that language being published, and unpublishing that language unpublishes
|
||||
/// the entire document.</para>
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
bool IsMandatory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of a fallback language.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The fallback language can be used in multi-lingual scenarios, to help
|
||||
/// define fallback strategies when a value does not exist for a requested language.</para>
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
int? FallbackLanguageId { get; set; }
|
||||
}
|
||||
}
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface ILanguage : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Iso Code for the Language
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string IsoCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Culture Name for the Language
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string CultureName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="CultureInfo"/> object for the current Language
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
CultureInfo CultureInfo { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,79 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Macro
|
||||
/// </summary>
|
||||
public interface IMacro : IEntity, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the alias of the Macro
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the Macro
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Macro can be used in an Editor
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool UseInEditor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Cache Duration for the Macro
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
int CacheDuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Macro should be Cached by Page
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool CacheByPage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Macro should be Cached Personally
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool CacheByMember { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Macro should be rendered in an Editor
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool DontRender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the path to the macro source to render
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string MacroSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the macro type
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
MacroTypes MacroType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of Macro Properties
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
MacroPropertyCollection Properties { get; }
|
||||
|
||||
///// <summary>
|
||||
///// Returns an enum <see cref="MacroTypes"/> based on the properties on the Macro
|
||||
///// </summary>
|
||||
///// <returns><see cref="MacroTypes"/></returns>
|
||||
//MacroTypes MacroType();
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Macro
|
||||
/// </summary>
|
||||
public interface IMacro : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the alias of the Macro
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the Macro
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Macro can be used in an Editor
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool UseInEditor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Cache Duration for the Macro
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
int CacheDuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Macro should be Cached by Page
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool CacheByPage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Macro should be Cached Personally
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool CacheByMember { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Macro should be rendered in an Editor
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool DontRender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to user control or the Control Type to render
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string ControlType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the assembly, which should be used by the Macro
|
||||
/// </summary>
|
||||
/// <remarks>Will usually only be filled if the ScriptFile is a Usercontrol</remarks>
|
||||
[DataMember]
|
||||
string ControlAssembly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the path to the Python file in use
|
||||
/// </summary>
|
||||
/// <remarks>Optional: Can only be one of three Script, Python or Xslt</remarks>
|
||||
[DataMember]
|
||||
string ScriptPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the Xslt file in use
|
||||
/// </summary>
|
||||
/// <remarks>Optional: Can only be one of three Script, Python or Xslt</remarks>
|
||||
[DataMember]
|
||||
string XsltPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of Macro Properties
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
MacroPropertyCollection Properties { get; }
|
||||
|
||||
///// <summary>
|
||||
///// Returns an enum <see cref="MacroTypes"/> based on the properties on the Macro
|
||||
///// </summary>
|
||||
///// <returns><see cref="MacroTypes"/></returns>
|
||||
//MacroTypes MacroType();
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,42 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Property for a Macro
|
||||
/// </summary>
|
||||
public interface IMacroProperty : IValueObject, IDeepCloneable, IRememberBeingDirty
|
||||
{
|
||||
[DataMember]
|
||||
int Id { get; set; }
|
||||
|
||||
[DataMember]
|
||||
Guid Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Alias of the Property
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the Property
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Sort Order of the Property
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parameter editor alias
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string EditorAlias { get; set; }
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Property for a Macro
|
||||
/// </summary>
|
||||
public interface IMacroProperty : IValueObject, IDeepCloneable, IRememberBeingDirty
|
||||
{
|
||||
[DataMember]
|
||||
int Id { get; set; }
|
||||
|
||||
[DataMember]
|
||||
Guid Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Alias of the Property
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the Property
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Sort Order of the Property
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parameter editor alias
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string EditorAlias { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a PropertyType (plugin) for a Macro
|
||||
/// </summary>
|
||||
internal interface IMacroPropertyType
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique Alias of the Property Type
|
||||
/// </summary>
|
||||
string Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the Assembly used to render the Property Type
|
||||
/// </summary>
|
||||
string RenderingAssembly { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the Type used to render the Property Type
|
||||
/// </summary>
|
||||
string RenderingType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Base Type for storing the PropertyType (Int32, String, Boolean)
|
||||
/// </summary>
|
||||
MacroPropertyTypeBaseTypes BaseType { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,27 @@
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IMedia : IContentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this Media object
|
||||
/// </summary>
|
||||
IMediaType ContentType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="IMediaType"/> for the current content object
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <remarks>Leaves PropertyTypes intact after change</remarks>
|
||||
void ChangeContentType(IMediaType contentType);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="IMediaType"/> for the current content object and removes PropertyTypes,
|
||||
/// which are not part of the new ContentType.
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <param name="clearProperties">Boolean indicating whether to clear PropertyTypes upon change</param>
|
||||
void ChangeContentType(IMediaType contentType, bool clearProperties);
|
||||
}
|
||||
}
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IMedia : IContentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this Media object
|
||||
/// </summary>
|
||||
IMediaType ContentType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="IMediaType"/> for the current content object
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <remarks>Leaves PropertyTypes intact after change</remarks>
|
||||
void ChangeContentType(IMediaType contentType);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="IMediaType"/> for the current content object and removes PropertyTypes,
|
||||
/// which are not part of the new ContentType.
|
||||
/// </summary>
|
||||
/// <param name="contentType">New ContentType for this content</param>
|
||||
/// <param name="clearProperties">Boolean indicating whether to clear PropertyTypes upon change</param>
|
||||
void ChangeContentType(IMediaType contentType, bool clearProperties);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a ContentType, which Media is based on
|
||||
/// </summary>
|
||||
public interface IMediaType : IContentTypeComposition
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <param name="newAlias"></param>
|
||||
/// <returns></returns>
|
||||
IMediaType DeepCloneWithResetIdentities(string newAlias);
|
||||
}
|
||||
}
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a ContentType, which Media is based on
|
||||
/// </summary>
|
||||
public interface IMediaType : IContentTypeComposition
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <param name="newAlias"></param>
|
||||
/// <returns></returns>
|
||||
IMediaType DeepCloneWithResetIdentities(string newAlias);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IMember : IContentBase, IMembershipUser, IHaveAdditionalData
|
||||
{
|
||||
/// <summary>
|
||||
/// String alias of the default ContentType
|
||||
/// </summary>
|
||||
string ContentTypeAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this content object
|
||||
/// </summary>
|
||||
IMemberType ContentType { get; }
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IMember : IContentBase, IMembershipUser
|
||||
{
|
||||
/// <summary>
|
||||
/// String alias of the default ContentType
|
||||
/// </summary>
|
||||
string ContentTypeAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this content object
|
||||
/// </summary>
|
||||
IMemberType ContentType { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a member type
|
||||
/// </summary>
|
||||
public interface IMemberGroup : IEntity, IRememberBeingDirty, IHaveAdditionalData
|
||||
public interface IMemberGroup : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the member group
|
||||
@@ -17,5 +17,10 @@ namespace Umbraco.Core.Models
|
||||
/// Profile of the user who created this Entity
|
||||
/// </summary>
|
||||
int CreatorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
IDictionary<string, object> AdditionalData { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,50 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a MemberType, which Member is based on
|
||||
/// </summary>
|
||||
public interface IMemberType : IContentTypeComposition
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool MemberCanEditProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is visible on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool MemberCanViewProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is marked as storing sensitive values on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool IsSensitiveProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetMemberCanEditProperty(string propertyTypeAlias, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is visible on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetMemberCanViewProperty(string propertyTypeAlias, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is a sensitive value on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetIsSensitiveProperty(string propertyTypeAlias, bool value);
|
||||
}
|
||||
}
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a MemberType, which Member is based on
|
||||
/// </summary>
|
||||
public interface IMemberType : IContentTypeComposition
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool MemberCanEditProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is visible on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool MemberCanViewProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is marked as storing sensitive values on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool IsSensitiveProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetMemberCanEditProperty(string propertyTypeAlias, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is visible on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetMemberCanViewProperty(string propertyTypeAlias, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is a sensitive value on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetIsSensitiveProperty(string propertyTypeAlias, bool value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using Semver;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IMigrationEntry : IEntity, IRememberBeingDirty
|
||||
public interface IMigrationEntry : IAggregateRoot, IRememberBeingDirty
|
||||
{
|
||||
string MigrationName { get; set; }
|
||||
SemVersion Version { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,4 @@
|
||||
{
|
||||
PartialViewType ViewType { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cached content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>SD: A replacement for INode which needs to occur since INode doesn't contain the document type alias
|
||||
/// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable).</para>
|
||||
/// <para>Stephan: initially, that was for cached published content only. Now, we're using it also for
|
||||
/// cached preview (so, maybe unpublished) content. A better name would therefore be ICachedContent, as
|
||||
/// has been suggested. However, can't change now. Maybe in v7?</para>
|
||||
/// </remarks>
|
||||
public interface IPublishedContent
|
||||
{
|
||||
#region ContentSet
|
||||
|
||||
// Because of http://issues.umbraco.org/issue/U4-1797 and in order to implement
|
||||
// Index() and methods that derive from it such as IsFirst(), IsLast(), etc... all
|
||||
// content items must know about their containing content set.
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content set to which the content belongs.
|
||||
/// </summary>
|
||||
/// <remarks>The default set consists in the siblings of the content (including the content
|
||||
/// itself) ordered by <c>sortOrder</c>.</remarks>
|
||||
IEnumerable<IPublishedContent> ContentSet { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ContentType
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type.
|
||||
/// </summary>
|
||||
PublishedContentType ContentType { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content
|
||||
|
||||
int Id { get; }
|
||||
int TemplateId { get; }
|
||||
int SortOrder { get; }
|
||||
string Name { get; }
|
||||
string UrlName { get; }
|
||||
string DocumentTypeAlias { get; }
|
||||
int DocumentTypeId { get; }
|
||||
string WriterName { get; }
|
||||
string CreatorName { get; }
|
||||
int WriterId { get; }
|
||||
int CreatorId { get; }
|
||||
string Path { get; }
|
||||
DateTime CreateDate { get; }
|
||||
DateTime UpdateDate { get; }
|
||||
Guid Version { get; }
|
||||
int Level { get; }
|
||||
string Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is a content (aka a document) or a media.
|
||||
/// </summary>
|
||||
PublishedItemType ItemType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is draft.
|
||||
/// </summary>
|
||||
/// <remarks>A content is draft when it is the unpublished version of a content, which may
|
||||
/// have a published version, or not.</remarks>
|
||||
bool IsDraft { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the published content within its current owning content set.
|
||||
/// </summary>
|
||||
/// <returns>The index of the published content within its current owning content set.</returns>
|
||||
int GetIndex();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tree
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of the content.
|
||||
/// </summary>
|
||||
/// <remarks>The parent of root content is <c>null</c>.</remarks>
|
||||
IPublishedContent Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of the content.
|
||||
/// </summary>
|
||||
/// <remarks>Children are sorted by their sortOrder.</remarks>
|
||||
IEnumerable<IPublishedContent> Children { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the properties of the content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Contains one <c>IPublishedProperty</c> for each property defined for the content type, including
|
||||
/// inherited properties. Some properties may have no value.</para>
|
||||
/// <para>The properties collection of an IPublishedContent instance should be read-only ie it is illegal
|
||||
/// to add properties to the collection.</para>
|
||||
/// </remarks>
|
||||
ICollection<IPublishedProperty> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property identified by its alias.
|
||||
/// </summary>
|
||||
/// <param name="alias">The property alias.</param>
|
||||
/// <returns>The property identified by the alias.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If the content type has no property with that alias, including inherited properties, returns <c>null</c>,</para>
|
||||
/// <para>otherwise return a property -- that may have no value (ie <c>HasValue</c> is <c>false</c>).</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
IPublishedProperty GetProperty(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property identified by its alias.
|
||||
/// </summary>
|
||||
/// <param name="alias">The property alias.</param>
|
||||
/// <param name="recurse">A value indicating whether to navigate the tree upwards until a property with a value is found.</param>
|
||||
/// <returns>The property identified by the alias.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Navigate the tree upwards and look for a property with that alias and with a value (ie <c>HasValue</c> is <c>true</c>).
|
||||
/// If found, return the property. If no property with that alias is found, having a value or not, return <c>null</c>. Otherwise
|
||||
/// return the first property that was found with the alias but had no value (ie <c>HasValue</c> is <c>false</c>).</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
IPublishedProperty GetProperty(string alias, bool recurse);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a property identified by its alias.
|
||||
/// </summary>
|
||||
/// <param name="alias">The property alias.</param>
|
||||
/// <returns>The value of the property identified by the alias.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If <c>GetProperty(alias)</c> is <c>null</c> then returns <c>null</c> else return <c>GetProperty(alias).Value</c>.</para>
|
||||
/// <para>So if the property has no value, returns the default value for that property type.</para>
|
||||
/// <para>This one is defined here really because we cannot define index extension methods, but all it should do is:
|
||||
/// <code>var p = GetProperty(alias); return p == null ? null : p.Value;</code> and nothing else.</para>
|
||||
/// <para>The recursive syntax (eg "_title") is _not_ supported here.</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
object this[string alias] { get; } // todo - should obsolete this[alias] (when?)
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cached content with a GUID key.
|
||||
/// </summary>
|
||||
/// <remarks>This is temporary, because we cannot add the Key property to IPublishedContent without
|
||||
/// breaking backward compatibility. With v8, it will be merged into IPublishedContent.</remarks>
|
||||
public interface IPublishedContentWithKey : IPublishedContent
|
||||
{
|
||||
Guid Key { get; }
|
||||
}
|
||||
}
|
||||
+60
-62
@@ -1,62 +1,60 @@
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a property of an <c>IPublishedElement</c>.
|
||||
/// </summary>
|
||||
public interface IPublishedProperty
|
||||
{
|
||||
PublishedPropertyType PropertyType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the alias of the property.
|
||||
/// </summary>
|
||||
string Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the property has a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This is somewhat implementation-dependent -- depending on whatever IPublishedCache considers
|
||||
/// a missing value.</para>
|
||||
/// <para>The XmlPublishedCache raw values are strings, and it will consider missing, null or empty (and
|
||||
/// that includes whitespace-only) strings as "no value".</para>
|
||||
/// <para>Other caches that get their raw value from the database would consider that a property has "no
|
||||
/// value" if it is missing, null, or an empty string (including whitespace-only).</para>
|
||||
/// </remarks>
|
||||
bool HasValue(string culture = null, string segment = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The source value is whatever was passed to the property when it was instanciated, and it is
|
||||
/// somewhat implementation-dependent -- depending on how the IPublishedCache is implemented.</para>
|
||||
/// <para>The XmlPublishedCache source values are strings exclusively since they come from the Xml cache.</para>
|
||||
/// <para>For other caches that get their source value from the database, it would be either a string,
|
||||
/// an integer (Int32), a date and time (DateTime) or a decimal (double).</para>
|
||||
/// <para>If you're using that value, you're probably wrong, unless you're doing some internal
|
||||
/// Umbraco stuff.</para>
|
||||
/// </remarks>
|
||||
object GetSourceValue(string culture = null, string segment = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The value is what you want to use when rendering content in an MVC view ie in C#.</para>
|
||||
/// <para>It can be null, or any type of CLR object.</para>
|
||||
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
|
||||
/// </remarks>
|
||||
object GetValue(string culture = null, string segment = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XPath value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The XPath value is what you want to use when navigating content via XPath eg in the XSLT engine.</para>
|
||||
/// <para>It must be either null, or a string, or an XPathNavigator.</para>
|
||||
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
|
||||
/// </remarks>
|
||||
object GetXPathValue(string culture = null, string segment = null);
|
||||
}
|
||||
}
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a property of an <c>IPublishedContent</c>.
|
||||
/// </summary>
|
||||
public interface IPublishedProperty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the alias of the property.
|
||||
/// </summary>
|
||||
string PropertyTypeAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the property has a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This is somewhat implementation-dependent -- depending on whatever IPublishedCache considers
|
||||
/// a missing value.</para>
|
||||
/// <para>The XmlPublishedCache raw values are strings, and it will consider missing, null or empty (and
|
||||
/// that includes whitespace-only) strings as "no value".</para>
|
||||
/// <para>Other caches that get their raw value from the database would consider that a property has "no
|
||||
/// value" if it is missing, null, or an empty string (including whitespace-only).</para>
|
||||
/// </remarks>
|
||||
bool HasValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The data value is whatever was passed to the property when it was instanciated, and it is
|
||||
/// somewhat implementation-dependent -- depending on how the IPublishedCache is implemented.</para>
|
||||
/// <para>The XmlPublishedCache raw values are strings exclusively since they come from the Xml cache.</para>
|
||||
/// <para>For other caches that get their raw value from the database, it would be either a string,
|
||||
/// an integer (Int32), or a date and time (DateTime).</para>
|
||||
/// <para>If you're using that value, you're probably wrong, unless you're doing some internal
|
||||
/// Umbraco stuff.</para>
|
||||
/// </remarks>
|
||||
object DataValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The value is what you want to use when rendering content in an MVC view ie in C#.</para>
|
||||
/// <para>It can be null, or any type of CLR object.</para>
|
||||
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
|
||||
/// </remarks>
|
||||
object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XPath value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The XPath value is what you want to use when navigating content via XPath eg in the XSLT engine.</para>
|
||||
/// <para>It must be either null, or a string, or an XPathNavigator.</para>
|
||||
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
|
||||
/// </remarks>
|
||||
object XPathValue { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a redirect url.
|
||||
/// </summary>
|
||||
public interface IRedirectUrl : IEntity, IRememberBeingDirty
|
||||
public interface IRedirectUrl : IAggregateRoot, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the content item.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user