diff --git a/src/Umbraco.Core/ExpressionHelper.cs b/src/Umbraco.Core/ExpressionHelper.cs
index 1d84b2b59c..d3c9baf1b4 100644
--- a/src/Umbraco.Core/ExpressionHelper.cs
+++ b/src/Umbraco.Core/ExpressionHelper.cs
@@ -86,32 +86,30 @@ namespace Umbraco.Core
{
Expression expressionToCheck = lambdaExpression;
- bool done = false;
+ var loop = true;
- while (!done)
+ while (loop)
{
switch (expressionToCheck.NodeType)
{
case ExpressionType.Convert:
- expressionToCheck = ((UnaryExpression)expressionToCheck).Operand;
+ expressionToCheck = ((UnaryExpression) expressionToCheck).Operand;
break;
case ExpressionType.Lambda:
- expressionToCheck = ((LambdaExpression)expressionToCheck).Body;
+ expressionToCheck = ((LambdaExpression) expressionToCheck).Body;
break;
case ExpressionType.MemberAccess:
- var memberExpression = ((MemberExpression)expressionToCheck);
+ var memberExpression = (MemberExpression) expressionToCheck;
if (memberExpression.Expression.NodeType != ExpressionType.Parameter &&
memberExpression.Expression.NodeType != ExpressionType.Convert)
{
- throw new ArgumentException(string.Format("Expression '{0}' must resolve to top-level member and not any child object's properties. Use a custom resolver on the child type or the AfterMap option instead.", lambdaExpression), "lambdaExpression");
+ throw new ArgumentException($"Expression '{lambdaExpression}' must resolve to top-level member and not any child object's properties. Use a custom resolver on the child type or the AfterMap option instead.", "lambdaExpression");
}
- MemberInfo member = memberExpression.Member;
-
- return member;
+ return memberExpression.Member;
default:
- done = true;
+ loop = false;
break;
}
}
diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index 3699b1b082..fbdec985a3 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -157,6 +157,9 @@ namespace Umbraco.Core.Models
}
}
+ [IgnoreDataMember]
+ public bool Edited { get; internal set; }
+
///
/// Language of the data contained within this Content object.
///
@@ -207,6 +210,21 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
public IContentType ContentType => _contentType;
+ [IgnoreDataMember]
+ public DateTime? PublishDate { get; internal set; }
+
+ [IgnoreDataMember]
+ public int? PublisherId { get; internal set; }
+
+ [IgnoreDataMember]
+ public ITemplate PublishTemplate { get; internal set; }
+
+ [IgnoreDataMember]
+ public string PublishName { get; internal set; }
+
+ [DataMember]
+ public bool Blueprint { get; internal set; }
+
///
/// Changes the for the current content object
///
@@ -242,23 +260,6 @@ namespace Umbraco.Core.Models
ChangeContentType(contentType);
}
- ///
- /// Gets or sets the unique identifier of the published version, if any.
- ///
- [IgnoreDataMember]
- public Guid PublishedVersionGuid { get; internal set; }
-
- ///
- /// Gets a value indicating whether the content has a published version.
- ///
- public bool HasPublishedVersion => PublishedVersionGuid != default;
-
- [IgnoreDataMember]
- internal DateTime PublishedDate { get; set; }
-
- [DataMember]
- public bool IsBlueprint { get; internal set; }
-
public override void ResetDirtyProperties(bool rememberDirty)
{
base.ResetDirtyProperties(rememberDirty);
@@ -301,8 +302,6 @@ namespace Umbraco.Core.Models
foreach (var property in clone.Properties)
property.ResetIdentity();
- clone.PublishedVersionGuid = Guid.Empty;
-
return clone;
}
diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs
index 7b14a9c4e1..ef3b1f4802 100644
--- a/src/Umbraco.Core/Models/ContentBase.cs
+++ b/src/Umbraco.Core/Models/ContentBase.cs
@@ -360,6 +360,109 @@ namespace Umbraco.Core.Models
property.PublishAllValues();
}
+ internal virtual void RollbackValues(IContentBase other)
+ {
+ // clear all existing properties
+ ClearEditValues(null, null);
+
+ // copy other properties
+ var otherProperties = other.Properties;
+ foreach (var otherProperty in otherProperties)
+ {
+ var alias = otherProperty.PropertyType.Alias;
+ SetValue(alias, otherProperty.GetValue(true));
+ }
+ }
+
+ internal virtual void RollbackValues(IContentBase other, int? nLanguageId)
+ {
+ if (!nLanguageId.HasValue)
+ {
+ RollbackValues(other);
+ return;
+ }
+
+ var languageId = nLanguageId.Value;
+
+ // clear all existing properties
+ ClearEditValues(nLanguageId, null);
+
+ // copy other properties
+ var otherProperties = other.Properties;
+ foreach (var otherProperty in otherProperties)
+ {
+ var alias = otherProperty.PropertyType.Alias;
+ SetValue(alias, languageId, otherProperty.GetValue(languageId, true));
+ }
+ }
+
+ internal virtual void RollbackValues(IContentBase other, int? nLanguageId, string segment)
+ {
+ if (segment == null)
+ {
+ RollbackValues(other, nLanguageId);
+ return;
+ }
+
+ if (!nLanguageId.HasValue)
+ throw new ArgumentException("Cannot be null when segment is not null.", nameof(nLanguageId));
+
+ var languageId = nLanguageId.Value;
+
+ // clear all existing properties
+ ClearEditValues(nLanguageId, segment);
+
+ // copy other properties
+ var otherProperties = other.Properties;
+ foreach (var otherProperty in otherProperties)
+ {
+ var alias = otherProperty.PropertyType.Alias;
+ SetValue(alias, languageId, segment, otherProperty.GetValue(languageId, segment, true));
+ }
+ }
+
+ private void ClearEditValues()
+ {
+ // clear all existing properties
+ // note: use property.SetValue(), don't assign pvalue.EditValue, else change tracking fails
+ foreach (var property in Properties)
+ foreach (var pvalue in property.Values)
+ property.SetValue(pvalue.LanguageId, pvalue.Segment, null);
+ }
+
+ private void ClearEditValues(int? nLanguageId, string segment)
+ {
+ // clear all existing properties
+ // note: use property.SetValue(), don't assign pvalue.EditValue, else change tracking fails
+ foreach (var property in Properties)
+ foreach (var pvalue in property.Values)
+ if (pvalue.LanguageId == nLanguageId && pvalue.Segment == segment)
+ property.SetValue(pvalue.LanguageId, pvalue.Segment, null);
+ }
+
+ internal virtual void RollbackAllValues(IContentBase other)
+ {
+ // clear all existing properties
+ ClearEditValues();
+
+ // copy other properties
+ var otherProperties = other.Properties;
+ foreach (var otherProperty in otherProperties)
+ {
+ var alias = otherProperty.PropertyType.Alias;
+ foreach (var pvalue in otherProperty.Values)
+ {
+ // fixme can we update SetValue to accept null lang/segment and fallback?
+ if (!pvalue.LanguageId.HasValue)
+ SetValue(alias, pvalue.PublishedValue);
+ else if (pvalue.Segment == null)
+ SetValue(alias, pvalue.LanguageId.Value, pvalue.PublishedValue);
+ else
+ SetValue(alias, pvalue.LanguageId.Value, pvalue.Segment, pvalue.PublishedValue);
+ }
+ }
+ }
+
///
/// Sets the neutral (draft) value of a property.
///
diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs
index 123ba0ce1f..16687162cc 100644
--- a/src/Umbraco.Core/Models/ContentExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentExtensions.cs
@@ -201,8 +201,8 @@ namespace Umbraco.Core.Models
{
foreach (var propertyValue in property.Values)
{
- if (propertyValue.DraftValue is string draftString)
- propertyValue.DraftValue = draftString.ToValidXmlString();
+ if (propertyValue.EditValue is string editString)
+ propertyValue.EditValue = editString.ToValidXmlString();
if (propertyValue.PublishedValue is string publishedString)
propertyValue.PublishedValue = publishedString.ToValidXmlString();
}
@@ -478,133 +478,119 @@ namespace Umbraco.Core.Models
return userService.GetProfileById(content.WriterId);
}
+ ///
+ /// Gets the for the Writer of this content.
+ ///
+ public static IProfile GetWriterProfile(this IMedia content, IUserService userService)
+ {
+ return userService.GetProfileById(content.WriterId);
+ }
+
#endregion
- ///
- /// Checks whether an item has any published versions
- ///
- ///
- /// True if the content has any published versiom otherwise False
- [Obsolete("Use the HasPublishedVersion property.", false)]
- public static bool HasPublishedVersion(this IContent content)
- {
- return content.HasPublishedVersion;
- }
-
- #region Tag methods
-
-
+ #region Tags
///
- /// 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.
+ /// Sets tags.
///
- /// The content item to assign the tags to
- /// The property alias to assign the tags to
- /// The tags to assign
- /// True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones
- /// The group/category to assign the tags, the default value is "default"
- ///
- public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable tags, bool replaceTags, string tagGroup = "default")
- {
- content.SetTags(TagCacheStorageType.Csv, propertyTypeAlias, tags, replaceTags, tagGroup);
- }
-
- ///
- /// 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.
- ///
- /// The content item to assign the tags to
- /// The tag storage type in cache (default is csv)
- /// The property alias to assign the tags to
- /// The tags to assign
- /// True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones
- /// The group/category to assign the tags, the default value is "default"
- ///
- public static void SetTags(this IContentBase content, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable tags, bool replaceTags, string tagGroup = "default")
+ /// The content item.
+ /// The property alias.
+ /// The tags.
+ /// True to replace the tags with the specified tags or false to merge them with the currently assigned ones.
+ /// The tags group.
+ /// The tags storage type.
+ public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable tags, bool replaceTags = true, string tagGroup = "default", TagCacheStorageType storage = TagCacheStorageType.Csv)
{
var property = content.Properties[propertyTypeAlias];
if (property == null)
- {
throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias);
- }
- property.SetTags(storageType, propertyTypeAlias, tags, replaceTags, tagGroup);
+ property.SetTags(propertyTypeAlias, tags, replaceTags, tagGroup, storage);
}
// fixme - totally not ok with variants
- internal static void SetTags(this Property property, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable tags, bool replaceTags, string tagGroup = "default")
+ internal static void SetTags(this Property property, string propertyTypeAlias, IEnumerable tags, bool replaceTags = true, string tagGroup = "default", TagCacheStorageType storage = TagCacheStorageType.Csv)
{
if (property == null) throw new ArgumentNullException(nameof(property));
var trimmedTags = tags.Select(x => x.Trim()).ToArray();
+ var changes = property.TagChanges;
- property.TagSupport.Enable = true;
- property.TagSupport.Tags = trimmedTags.Select(x => new Tuple(x, tagGroup));
- property.TagSupport.Behavior = replaceTags ? PropertyTagBehavior.Replace : PropertyTagBehavior.Merge;
+ changes.Add(new PropertyTagChange
+ {
+ Type = replaceTags ? PropertyTagChange.ChangeType.Replace : PropertyTagChange.ChangeType.Merge,
+ Tags = trimmedTags.Select(x => new Tuple(x, tagGroup))
+ });
- //ensure the property value is set to the same thing
+ // ensure the property value is set to the same thing
if (replaceTags)
{
- switch (storageType)
+ switch (storage)
{
case TagCacheStorageType.Csv:
- property.SetValue(string.Join(",", trimmedTags));
+ property.SetValue(string.Join(",", trimmedTags)); // csv string
break;
case TagCacheStorageType.Json:
- //json array
- property.SetValue(JsonConvert.SerializeObject(trimmedTags));
+ property.SetValue(JsonConvert.SerializeObject(trimmedTags)); // json array
break;
}
-
}
- else
+ else // merge
{
- switch (storageType)
+ IEnumerable currentTags;
+ switch (storage)
{
case TagCacheStorageType.Csv:
- var currTags = property.GetValue().ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
- .Select(x => x.Trim());
- property.SetValue(string.Join(",", trimmedTags.Union(currTags)));
+ currentTags = property.GetValue().ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim());
+ property.SetValue(string.Join(",", currentTags.Union(trimmedTags))); // csv string
break;
case TagCacheStorageType.Json:
- var currJson = JsonConvert.DeserializeObject(property.GetValue().ToString());
- //need to append the new ones
- foreach (var tag in trimmedTags)
- {
- currJson.Add(tag);
- }
- //json array
- property.SetValue(JsonConvert.SerializeObject(currJson));
+ currentTags = JsonConvert.DeserializeObject(property.GetValue().ToString()).Select(x => x.ToString());
+ property.SetValue(JsonConvert.SerializeObject(currentTags.Union(trimmedTags).ToArray())); // json array
break;
}
}
}
///
- /// Remove any of the tags specified in the collection from the property if they are currently assigned.
+ /// Remove tags.
///
- ///
- ///
- ///
- /// The group/category that the tags are currently assigned to, the default value is "default"
+ /// The content item.
+ /// The property alias.
+ /// The tags.
+ /// The tags group.
// fixme - totally not ok with variants
public static void RemoveTags(this IContentBase content, string propertyTypeAlias, IEnumerable 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();
+ var changes = property.TagChanges;
- property.TagSupport.Behavior = PropertyTagBehavior.Remove;
- property.TagSupport.Enable = true;
- property.TagSupport.Tags = trimmedTags.Select(x => new Tuple(x, tagGroup));
+ changes.Add(new PropertyTagChange
+ {
+ Type = PropertyTagChange.ChangeType.Remove,
+ Tags = trimmedTags.Select(x => new Tuple(x, tagGroup))
+ });
- //set the property value
- var currTags = property.GetValue().ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
- .Select(x => x.Trim());
+ // set the property value
+ var value = property.GetValue()?.ToString();
+ if (string.IsNullOrWhiteSpace(value)) return;
- property.SetValue(string.Join(",", currTags.Except(trimmedTags)));
+ var storage = value.StartsWith("[") ? TagCacheStorageType.Json : TagCacheStorageType.Csv;
+ IEnumerable currentTags;
+ switch (storage)
+ {
+ case TagCacheStorageType.Csv:
+ currentTags = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim());
+ property.SetValue(string.Join(",", currentTags.Except(trimmedTags)));
+ break;
+ case TagCacheStorageType.Json:
+ currentTags = JsonConvert.DeserializeObject(property.GetValue().ToString()).Select(x => x.ToString());
+ property.SetValue(JsonConvert.SerializeObject(currentTags.Except(trimmedTags).ToArray())); // json array
+ break;
+ }
}
#endregion
diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs
index 882ce5cc25..d0f3edea8b 100644
--- a/src/Umbraco.Core/Models/ContentType.cs
+++ b/src/Umbraco.Core/Models/ContentType.cs
@@ -13,6 +13,9 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class ContentType : ContentTypeCompositionBase, IContentType
{
+ private static readonly Lazy Ps = new Lazy();
+ public const bool IsPublishingConst = true;
+
private int _defaultTemplate;
private IEnumerable _allowedTemplates;
@@ -48,8 +51,10 @@ namespace Umbraco.Core.Models
_allowedTemplates = new List();
}
- private static readonly Lazy Ps = new Lazy();
+ ///
+ public override bool IsPublishing => IsPublishingConst;
+ // ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo DefaultTemplateSelector = ExpressionHelper.GetPropertyInfo(x => x.DefaultTemplateId);
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index dfc94d3b2d..2737fd3405 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -19,6 +19,8 @@ namespace Umbraco.Core.Models
[DebuggerDisplay("Id: {Id}, Name: {Name}, Alias: {Alias}")]
public abstract class ContentTypeBase : Entity, IContentTypeBase
{
+ private static readonly Lazy Ps = new Lazy();
+
private Lazy _parentId;
private string _name;
private int _level;
@@ -29,7 +31,7 @@ namespace Umbraco.Core.Models
private string _icon = "icon-folder";
private string _thumbnail = "folder.png";
private int _creatorId;
- private bool _allowedAsRoot;
+ private bool _allowedAsRoot; // note: only one that's not 'pure element type'
private bool _isContainer;
private bool _trashed;
private PropertyGroupCollection _propertyGroups;
@@ -37,7 +39,6 @@ namespace Umbraco.Core.Models
private IEnumerable _allowedContentTypes;
private bool _hasPropertyTypeBeenRemoved;
-
protected ContentTypeBase(int parentId)
{
if (parentId == 0) throw new ArgumentOutOfRangeException(nameof(parentId));
@@ -45,14 +46,17 @@ namespace Umbraco.Core.Models
_parentId = new Lazy(() => parentId);
_allowedContentTypes = new List();
_propertyGroups = new PropertyGroupCollection();
- _propertyTypes = new PropertyTypeCollection();
- _propertyTypes.CollectionChanged += PropertyTypesChanged;
_additionalData = new Dictionary();
+
+ // actually OK as IsPublishing is constant
+ // ReSharper disable once VirtualMemberCallInConstructor
+ _propertyTypes = new PropertyTypeCollection(IsPublishing);
+ _propertyTypes.CollectionChanged += PropertyTypesChanged;
}
- protected ContentTypeBase(IContentTypeBase parent) : this(parent, null)
- {
- }
+ protected ContentTypeBase(IContentTypeBase parent)
+ : this(parent, null)
+ { }
protected ContentTypeBase(IContentTypeBase parent, string alias)
{
@@ -62,13 +66,28 @@ namespace Umbraco.Core.Models
_parentId = new Lazy(() => parent.Id);
_allowedContentTypes = new List();
_propertyGroups = new PropertyGroupCollection();
- _propertyTypes = new PropertyTypeCollection();
- _propertyTypes.CollectionChanged += PropertyTypesChanged;
_additionalData = new Dictionary();
+
+ // actually OK as IsPublishing is constant
+ // ReSharper disable once VirtualMemberCallInConstructor
+ _propertyTypes = new PropertyTypeCollection(IsPublishing);
+ _propertyTypes.CollectionChanged += PropertyTypesChanged;
}
- private static readonly Lazy Ps = new Lazy();
+ ///
+ /// Gets a value indicating whether the content type is publishing.
+ ///
+ ///
+ /// A publishing content type supports draft and published values for properties.
+ /// It is possible to retrieve either the draft (default) or published value of a property.
+ /// Setting the value always sets the draft value, which then needs to be published.
+ /// A non-publishing content type only supports one value for properties. Getting
+ /// the draft or published value of a property returns the same thing, and publishing
+ /// a value property has no effect.
+ ///
+ public abstract bool IsPublishing { get; }
+ // ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
@@ -96,7 +115,6 @@ namespace Umbraco.Core.Models
sorts => sorts.GetHashCode());
}
-
protected void PropertyGroupsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(Ps.Value.PropertyGroupCollectionSelector);
@@ -136,8 +154,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual string Name
{
- get { return _name; }
- set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); }
+ get => _name;
+ set => SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector);
}
///
@@ -146,8 +164,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual int Level //NOTE Is this relevant for a ContentType?
{
- get { return _level; }
- set { SetPropertyValueAndDetectChanges(value, ref _level, Ps.Value.LevelSelector); }
+ get => _level;
+ set => SetPropertyValueAndDetectChanges(value, ref _level, Ps.Value.LevelSelector);
}
///
@@ -156,8 +174,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual string Path //NOTE Is this relevant for a ContentType?
{
- get { return _path; }
- set { SetPropertyValueAndDetectChanges(value, ref _path, Ps.Value.PathSelector); }
+ get => _path;
+ set => SetPropertyValueAndDetectChanges(value, ref _path, Ps.Value.PathSelector);
}
///
@@ -166,14 +184,11 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual string Alias
{
- get { return _alias; }
- set
- {
- SetPropertyValueAndDetectChanges(
- value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase),
- ref _alias,
- Ps.Value.AliasSelector);
- }
+ get => _alias;
+ set => SetPropertyValueAndDetectChanges(
+ value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase),
+ ref _alias,
+ Ps.Value.AliasSelector);
}
///
@@ -182,8 +197,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual string Description
{
- get { return _description; }
- set { SetPropertyValueAndDetectChanges(value, ref _description, Ps.Value.DescriptionSelector); }
+ get => _description;
+ set => SetPropertyValueAndDetectChanges(value, ref _description, Ps.Value.DescriptionSelector);
}
///
@@ -192,8 +207,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual int SortOrder
{
- get { return _sortOrder; }
- set { SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector); }
+ get => _sortOrder;
+ set => SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector);
}
///
@@ -202,8 +217,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual string Icon
{
- get { return _icon; }
- set { SetPropertyValueAndDetectChanges(value, ref _icon, Ps.Value.IconSelector); }
+ get => _icon;
+ set => SetPropertyValueAndDetectChanges(value, ref _icon, Ps.Value.IconSelector);
}
///
@@ -212,8 +227,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual string Thumbnail
{
- get { return _thumbnail; }
- set { SetPropertyValueAndDetectChanges(value, ref _thumbnail, Ps.Value.ThumbnailSelector); }
+ get => _thumbnail;
+ set => SetPropertyValueAndDetectChanges(value, ref _thumbnail, Ps.Value.ThumbnailSelector);
}
///
@@ -222,8 +237,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual int CreatorId
{
- get { return _creatorId; }
- set { SetPropertyValueAndDetectChanges(value, ref _creatorId, Ps.Value.CreatorIdSelector); }
+ get => _creatorId;
+ set => SetPropertyValueAndDetectChanges(value, ref _creatorId, Ps.Value.CreatorIdSelector);
}
///
@@ -232,8 +247,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual bool AllowedAsRoot
{
- get { return _allowedAsRoot; }
- set { SetPropertyValueAndDetectChanges(value, ref _allowedAsRoot, Ps.Value.AllowedAsRootSelector); }
+ get => _allowedAsRoot;
+ set => SetPropertyValueAndDetectChanges(value, ref _allowedAsRoot, Ps.Value.AllowedAsRootSelector);
}
///
@@ -245,8 +260,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual bool IsContainer
{
- get { return _isContainer; }
- set { SetPropertyValueAndDetectChanges(value, ref _isContainer, Ps.Value.IsContainerSelector); }
+ get => _isContainer;
+ set => SetPropertyValueAndDetectChanges(value, ref _isContainer, Ps.Value.IsContainerSelector);
}
///
@@ -256,8 +271,8 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual bool Trashed //NOTE Is this relevant for a ContentType?
{
- get { return _trashed; }
- set { SetPropertyValueAndDetectChanges(value, ref _trashed, Ps.Value.TrashedSelector); }
+ get => _trashed;
+ set => SetPropertyValueAndDetectChanges(value, ref _trashed, Ps.Value.TrashedSelector);
}
private readonly IDictionary _additionalData;
@@ -265,10 +280,7 @@ namespace Umbraco.Core.Models
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
///
[EditorBrowsable(EditorBrowsableState.Never)]
- IDictionary IUmbracoEntity.AdditionalData
- {
- get { return _additionalData; }
- }
+ IDictionary IUmbracoEntity.AdditionalData => _additionalData;
///
/// Gets or sets a list of integer Ids for allowed ContentTypes
@@ -276,12 +288,9 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual IEnumerable AllowedContentTypes
{
- get { return _allowedContentTypes; }
- set
- {
- SetPropertyValueAndDetectChanges(value, ref _allowedContentTypes, Ps.Value.AllowedContentTypesSelector,
- Ps.Value.ContentTypeSortComparer);
- }
+ get => _allowedContentTypes;
+ set => SetPropertyValueAndDetectChanges(value, ref _allowedContentTypes, Ps.Value.AllowedContentTypesSelector,
+ Ps.Value.ContentTypeSortComparer);
}
///
@@ -293,7 +302,7 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual PropertyGroupCollection PropertyGroups
{
- get { return _propertyGroups; }
+ get => _propertyGroups;
set
{
_propertyGroups = value;
@@ -320,10 +329,10 @@ namespace Umbraco.Core.Models
///
public IEnumerable NoGroupPropertyTypes
{
- get { return _propertyTypes; }
+ get => _propertyTypes;
set
{
- _propertyTypes = new PropertyTypeCollection(value);
+ _propertyTypes = new PropertyTypeCollection(IsPublishing, value);
_propertyTypes.CollectionChanged += PropertyTypesChanged;
PropertyTypesChanged(_propertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
@@ -339,7 +348,7 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
internal bool HasPropertyTypeBeenRemoved
{
- get { return _hasPropertyTypeBeenRemoved; }
+ get => _hasPropertyTypeBeenRemoved;
private set
{
_hasPropertyTypeBeenRemoved = value;
@@ -416,10 +425,8 @@ namespace Umbraco.Core.Models
propertyType.PropertyGroupId = newPropertyGroup == null ? null : new Lazy(() => newPropertyGroup.Id, false);
// remove from old group, if any - add to new group, if any
- if (oldPropertyGroup != null)
- oldPropertyGroup.PropertyTypes.RemoveItem(propertyTypeAlias);
- if (newPropertyGroup != null)
- newPropertyGroup.PropertyTypes.Add(propertyType);
+ oldPropertyGroup?.PropertyTypes.RemoveItem(propertyTypeAlias);
+ newPropertyGroup?.PropertyTypes.Add(propertyType);
return true;
}
@@ -484,10 +491,7 @@ namespace Umbraco.Core.Models
/// PropertyTypes that are not part of a PropertyGroup
///
[IgnoreDataMember]
- internal PropertyTypeCollection PropertyTypeCollection
- {
- get { return _propertyTypes; }
- }
+ internal PropertyTypeCollection PropertyTypeCollection => _propertyTypes;
///
/// Indicates whether a specific property on the current entity is dirty.
diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
index 0e69d0a719..8fdd7904e4 100644
--- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
@@ -14,17 +14,17 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentTypeComposition
{
+ private static readonly Lazy Ps = new Lazy();
+
private List _contentTypeComposition = new List();
internal List RemovedContentTypeKeyTracker = new List();
protected ContentTypeCompositionBase(int parentId) : base(parentId)
- {
- }
+ { }
protected ContentTypeCompositionBase(IContentTypeComposition parent)
: this(parent, null)
- {
- }
+ { }
protected ContentTypeCompositionBase(IContentTypeComposition parent, string alias)
: base(parent, alias)
@@ -32,8 +32,7 @@ namespace Umbraco.Core.Models
AddContentType(parent);
}
- private static readonly Lazy Ps = new Lazy();
-
+ // ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo ContentTypeCompositionSelector =
@@ -46,7 +45,7 @@ namespace Umbraco.Core.Models
[DataMember]
public IEnumerable ContentTypeComposition
{
- get { return _contentTypeComposition; }
+ get => _contentTypeComposition;
set
{
_contentTypeComposition = value.ToList();
@@ -181,7 +180,7 @@ namespace Umbraco.Core.Models
return null;
// create the new group
- var group = new PropertyGroup { Name = name, SortOrder = 0 };
+ 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
diff --git a/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs b/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs
index d5d1fa2f78..4d69a78c16 100644
--- a/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs
+++ b/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs
@@ -2,7 +2,7 @@
namespace Umbraco.Core.Models.EntityBase
{
- public interface IUmbracoEntity : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
+ public interface IUmbracoEntity : IAggregateRoot, IRememberBeingDirty
{
///
/// Profile of the user who created this Entity
diff --git a/src/Umbraco.Core/Models/EntityExtensions.cs b/src/Umbraco.Core/Models/EntityExtensions.cs
index 1111130fde..0759426062 100644
--- a/src/Umbraco.Core/Models/EntityExtensions.cs
+++ b/src/Umbraco.Core/Models/EntityExtensions.cs
@@ -1,5 +1,4 @@
-using System.Linq;
-using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
@@ -17,7 +16,7 @@ namespace Umbraco.Core.Models
///
public static bool IsNewEntity(this IEntity entity)
{
- var dirty = (IRememberBeingDirty)entity;
+ var dirty = (IRememberBeingDirty) entity;
return dirty.WasPropertyDirty("Id");
}
}
diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs
index 7ff68da8d0..bd21037cd6 100644
--- a/src/Umbraco.Core/Models/IContent.cs
+++ b/src/Umbraco.Core/Models/IContent.cs
@@ -9,40 +9,75 @@ namespace Umbraco.Core.Models
public interface IContent : IContentBase
{
///
- /// Gets or sets the template used by the Content.
- /// This is used to override the default one from the ContentType.
+ /// Gets or sets the template used to render the content.
///
ITemplate Template { get; set; }
///
- /// Boolean indicating whether the Content is Published or not
+ /// Gets a value indicating whether the content is published.
///
bool Published { get; }
+ ///
+ /// Gets a value indicating whether the content has been edited.
+ ///
+ bool Edited { get; }
+
+ ///
+ /// Gets a value indicating whether the content item is a blueprint.
+ ///
+ bool Blueprint { get; }
+
+ ///
+ /// Gets the template used to render the published version of the content.
+ ///
+ /// When editing the content, the template can change, but this will
+ /// not until the content is published.
+ ITemplate PublishTemplate { get; }
+
+ ///
+ /// Gets the name of the published version of the content.
+ ///
+ /// When editing the content, the name can change, but this will
+ /// not until the content is published.
+ string PublishName { get; }
+
+ ///
+ /// Gets the identifier of the user who published the content.
+ ///
+ int? PublisherId { get; }
+
+ ///
+ /// Gets the date and time the content was published.
+ ///
+ DateTime? PublishDate { get; }
+
[Obsolete("This will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
string Language { get; set; }
///
- /// Gets or Sets the date the Content should be released and thus be published
+ /// Gets or sets the date and time the content item should be published.
///
DateTime? ReleaseDate { get; set; }
///
- /// Gets or Sets the date the Content should expire and thus be unpublished
+ /// Gets or sets the date and time the content should be unpublished.
///
DateTime? ExpireDate { get; set; }
///
- /// Gets the ContentType used by this content object
+ /// Gets the content type of this content.
///
IContentType ContentType { get; }
///
- /// Gets the current status of the Content
+ /// Gets the current status of the content.
///
ContentStatus Status { get; }
+ // fixme - these two should move to some kind of service
+
///
/// Changes the for the current content object
///
@@ -63,20 +98,5 @@ namespace Umbraco.Core.Models
///
///
IContent DeepCloneWithResetIdentities();
-
- ///
- /// Gets a value indicating whether the content has a published version.
- ///
- bool HasPublishedVersion { get; }
-
- ///
- /// Gets the unique identifier of the published version, if any.
- ///
- Guid PublishedVersionGuid { get; }
-
- ///
- /// Gets a value indicating whether the content item is a blueprint.
- ///
- bool IsBlueprint { get; }
}
}
diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs
index 406b7e9671..8feda0d733 100644
--- a/src/Umbraco.Core/Models/IContentBase.cs
+++ b/src/Umbraco.Core/Models/IContentBase.cs
@@ -44,7 +44,7 @@ namespace Umbraco.Core.Models
IEnumerable PropertyTypes { get; }
///
- /// Indicates whether the content object has a property with the supplied alias
+ /// Gets a value indicating whether the content object has a property with the supplied alias.
///
/// Alias of the PropertyType
/// True if Property with given alias exists, otherwise False
diff --git a/src/Umbraco.Core/Models/MediaType.cs b/src/Umbraco.Core/Models/MediaType.cs
index e270306de5..bc8b7c4c89 100644
--- a/src/Umbraco.Core/Models/MediaType.cs
+++ b/src/Umbraco.Core/Models/MediaType.cs
@@ -10,6 +10,8 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class MediaType : ContentTypeCompositionBase, IMediaType
{
+ public const bool IsPublishingConst = false;
+
///
/// Constuctor for creating a MediaType with the parent's id.
///
@@ -39,6 +41,9 @@ namespace Umbraco.Core.Models
{
}
+ ///
+ public override bool IsPublishing => IsPublishingConst;
+
///
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
///
diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs
index e621abdf61..553ae181cc 100644
--- a/src/Umbraco.Core/Models/Member.cs
+++ b/src/Umbraco.Core/Models/Member.cs
@@ -536,66 +536,37 @@ namespace Umbraco.Core.Models
private Attempt WarnIfPropertyTypeNotFoundOnGet(string propertyAlias, string propertyName, T defaultVal)
{
- Action doLog = () => Current.Logger.Warn(
- "Trying to access the '"
- + propertyName
- + "' property on "
- + typeof(Member)
- + " but the "
- + propertyAlias
- + " property does not exist on the member type so a default value is returned. Ensure that you have a property type with alias: "
- + propertyAlias
- + " configured on your member type in order to use the '"
- + propertyName
- + "' property on the model correctly.");
+ void DoLog(string logPropertyAlias, string logPropertyName)
+ => Current.Logger.Warn($"Trying to access the '{logPropertyName}' property on " + typeof(Member)
+ + $" but the {logPropertyAlias} property does not exist on the member type so a default value is returned. Ensure that you have a property type with alias: "
+ + logPropertyAlias + $" configured on your member type in order to use the '{logPropertyName}' property on the model correctly.");
- //if the property doesn't exist, then do the logging and return a failure
+ // if the property doesn't exist,
if (Properties.Contains(propertyAlias) == false)
{
- //we'll put a warn in the log if this entity has been persisted
+ // put a warn in the log if this entity has been persisted
+ // then return a failure
if (HasIdentity)
- {
- doLog();
- }
- return Attempt.Fail(defaultVal);
- }
-
- //if the property doesn't have an identity but we do, then do logging and return failure
- var prop = Properties.Single(x => x.Alias == propertyAlias);
- if (prop.HasIdentity == false && HasIdentity)
- {
- doLog();
+ DoLog(propertyAlias, propertyName);
return Attempt.Fail(defaultVal);
}
return Attempt.Succeed();
}
- private bool WarnIfPropertyTypeNotFoundOnSet(string propertyAlias, string propertyname)
+ private bool WarnIfPropertyTypeNotFoundOnSet(string propertyAlias, string propertyName)
{
- Action doLog = () => Current.Logger.Warn("An attempt was made to set a value on the property '"
- + propertyname
- + "' on type "
- + typeof(Member)
- + " but the property type "
- + propertyAlias
- + " does not exist on the member type, ensure that this property type exists so that setting this property works correctly.");
+ void DoLog(string logPropertyAlias, string logPropertyName)
+ => Current.Logger.Warn($"An attempt was made to set a value on the property '{logPropertyName}' on type " + typeof(Member)
+ + $" but the property type {logPropertyAlias} does not exist on the member type, ensure that this property type exists so that setting this property works correctly.");
- //if the property doesn't exist, then do the logging and return a failure
+ // if the property doesn't exist,
if (Properties.Contains(propertyAlias) == false)
{
+ // put a warn in the log if this entity has been persisted
+ // then return a failure
if (HasIdentity)
- {
- doLog();
- }
- return false;
- }
-
- //if the property doesn't have an identity but we do, then do logging and return failure
- var prop = Properties.Single(x => x.Alias == propertyAlias);
- if (prop.HasIdentity == false && HasIdentity)
- {
- doLog();
+ DoLog(propertyAlias, propertyName);
return false;
}
diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs
index e09a743b66..2ad2246269 100644
--- a/src/Umbraco.Core/Models/MemberType.cs
+++ b/src/Umbraco.Core/Models/MemberType.cs
@@ -12,6 +12,9 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class MemberType : ContentTypeCompositionBase, IMemberType
{
+ private static readonly Lazy Ps = new Lazy();
+ public const bool IsPublishingConst = false;
+
//Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId
private string _alias;
@@ -30,7 +33,8 @@ namespace Umbraco.Core.Models
MemberTypePropertyTypes = new Dictionary();
}
- private static readonly Lazy Ps = new Lazy();
+ ///
+ public override bool IsPublishing => IsPublishingConst;
private class PropertySelectors
{
diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index c3d1a3d06c..063da227a7 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Models
public class Property : Entity
{
private PropertyType _propertyType;
- private readonly PropertyTags _tagSupport = new PropertyTags(); // fixme allocating even if no support?
+ private List _tagChanges;
private List _values = new List();
private PropertyValue _pvalue;
@@ -41,13 +41,13 @@ namespace Umbraco.Core.Models
public class PropertyValue
{
- public int? LanguageId { get; set; }
- public string Segment { get; set; }
- public object PublishedValue { get; set; }
- public object DraftValue { get; set; }
+ public int? LanguageId { get; internal set; }
+ public string Segment { get; internal set; }
+ public object EditValue { get; internal set; }
+ public object PublishedValue { get; internal set; }
public PropertyValue Clone()
- => new PropertyValue { LanguageId = LanguageId, Segment = Segment, PublishedValue = PublishedValue, DraftValue = DraftValue };
+ => new PropertyValue { LanguageId = LanguageId, Segment = Segment, PublishedValue = PublishedValue, EditValue = EditValue };
}
// ReSharper disable once ClassNeverInstantiated.Local
@@ -105,9 +105,14 @@ namespace Umbraco.Core.Models
}
///
- /// Returns the instance of the tag support, by default tags are not enabled
+ /// Gets the tag changes.
///
- internal PropertyTags TagSupport => _tagSupport;
+ internal List TagChanges => _tagChanges ?? (_tagChanges = new List());
+
+ ///
+ /// Gets a value indicating whether the property has tag changes.
+ ///
+ internal bool HasTagChanges => _tagChanges != null;
///
/// Returns the Alias of the PropertyType, which this Property is based on
@@ -135,8 +140,7 @@ namespace Umbraco.Core.Models
///
public object GetValue(bool published = false)
{
- if (_pvalue == null) return null;
- return published ? _pvalue.PublishedValue : _pvalue.DraftValue;
+ return _pvalue == null ? null : GetPropertyValue(_pvalue, published);
}
///
@@ -145,8 +149,8 @@ namespace Umbraco.Core.Models
public object GetValue(int languageId, bool published = false)
{
if (_lvalues == null) return null;
- if (!_lvalues.TryGetValue(languageId, out var value)) return null;
- return published ? value.PublishedValue : value.DraftValue;
+ if (!_lvalues.TryGetValue(languageId, out var pvalue)) return null;
+ return GetPropertyValue(pvalue, published);
}
///
@@ -156,8 +160,15 @@ namespace Umbraco.Core.Models
{
if (_svalues == null) return null;
if (!_svalues.TryGetValue(languageId, out var svalues)) return null;
- if (!svalues.TryGetValue(segment, out var value)) return null;
- return published ? value.PublishedValue : value.DraftValue;
+ if (!svalues.TryGetValue(segment, out var pvalue)) return null;
+ return GetPropertyValue(pvalue, published);
+ }
+
+ private object GetPropertyValue(PropertyValue pvalue, bool published)
+ {
+ return _propertyType.IsPublishing
+ ? (published ? pvalue.PublishedValue : pvalue.EditValue)
+ : pvalue.EditValue;
}
internal void PublishValues()
@@ -208,26 +219,24 @@ namespace Umbraco.Core.Models
private void PublishPropertyValue(PropertyValue pvalue)
{
+ if (!_propertyType.IsPublishing)
+ throw new NotSupportedException("Property type does not support publishing.");
var origValue = pvalue.PublishedValue;
- pvalue.PublishedValue = ConvertSetValue(pvalue.DraftValue);
- DetectChanges(pvalue.DraftValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, false);
+ pvalue.PublishedValue = ConvertSetValue(pvalue.EditValue);
+ DetectChanges(pvalue.EditValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, false);
}
///
- /// Sets a (draft) neutral value.
+ /// Sets a (edit) neutral value.
///
public void SetValue(object value)
{
(var pvalue, var change) = GetPropertyValue(true);
-
- var origValue = pvalue.DraftValue;
- pvalue.DraftValue = ConvertSetValue(value);
-
- DetectChanges(_pvalue.DraftValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, change);
+ SetPropertyValue(pvalue, value, change);
}
///
- /// Sets a (draft) culture value.
+ /// Sets a (edit) culture value.
///
public void SetValue(int? nLanguageId, object value)
{
@@ -240,15 +249,11 @@ namespace Umbraco.Core.Models
var languageId = nLanguageId.Value;
(var pvalue, var change) = GetPropertyValue(languageId, true);
-
- var origValue = pvalue.DraftValue;
- pvalue.DraftValue = ConvertSetValue(value);
-
- DetectChanges(pvalue.DraftValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, change);
+ SetPropertyValue(pvalue, value, change);
}
///
- /// Sets a (draft) segment value.
+ /// Sets a (edit) segment value.
///
public void SetValue(int? nLanguageId, string segment, object value)
{
@@ -263,11 +268,61 @@ namespace Umbraco.Core.Models
var languageId = nLanguageId.Value;
(var pvalue, var change) = GetPropertyValue(languageId, segment, true);
+ SetPropertyValue(pvalue, value, change);
+ }
- var origValue = pvalue.DraftValue;
- pvalue.DraftValue = ConvertSetValue(value);
+ private void SetPropertyValue(PropertyValue pvalue, object value, bool change)
+ {
+ var origValue = pvalue.EditValue;
+ var setValue = ConvertSetValue(value);
- DetectChanges(pvalue.DraftValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, change);
+ pvalue.EditValue = setValue;
+
+ DetectChanges(setValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, change);
+ }
+
+ private void FactorySetValue(bool published, object value)
+ {
+ (var pvalue, _) = GetPropertyValue(true);
+ FactorySetPropertyValue(pvalue, published, value);
+ }
+
+ private void FactorySetValue(int? nLanguageId, bool published, object value)
+ {
+ if (nLanguageId == null)
+ {
+ FactorySetValue(published, value);
+ return;
+ }
+
+ var languageId = nLanguageId.Value;
+ (var pvalue, _) = GetPropertyValue(languageId, true);
+ FactorySetPropertyValue(pvalue, published, value);
+ }
+
+ // bypasses all changes detection and is the *only* to set the published value
+ internal void FactorySetValue(int? nLanguageId, string segment, bool published, object value)
+ {
+ if (segment == null)
+ {
+ FactorySetValue(nLanguageId, published, value);
+ return;
+ }
+
+ if (!nLanguageId.HasValue)
+ throw new ArgumentException("Cannot be null when segment is not null.", nameof(nLanguageId));
+ var languageId = nLanguageId.Value;
+
+ (var pvalue, _) = GetPropertyValue(languageId, segment, true);
+ FactorySetPropertyValue(pvalue, published, value);
+ }
+
+ private void FactorySetPropertyValue(PropertyValue pvalue, bool published, object value)
+ {
+ if (published && _propertyType.IsPublishing)
+ pvalue.PublishedValue = value;
+ else
+ pvalue.EditValue = value;
}
private (PropertyValue, bool) GetPropertyValue(bool create)
@@ -379,7 +434,7 @@ namespace Umbraco.Core.Models
}
///
- /// Gets a value indicating whether the (draft) neutral value is valid.
+ /// Gets a value indicating whether the (edit) neutral value is valid.
///
/// An invalid value can be saved, but only valid values can be published.
public bool IsValid()
@@ -388,7 +443,7 @@ namespace Umbraco.Core.Models
}
///
- /// Gets a value indicating whether the (draft) culture value is valid.
+ /// Gets a value indicating whether the (edit) culture value is valid.
///
/// An invalid value can be saved, but only valid values can be published.
public bool IsValid(int languageId)
@@ -397,7 +452,7 @@ namespace Umbraco.Core.Models
}
///
- /// Gets a value indicating whether the (draft) segment value is valid.
+ /// Gets a value indicating whether the (edit) segment value is valid.
///
/// An invalid value can be saved, but only valid values can be published.
public bool IsValue(int languageId, string segment)
diff --git a/src/Umbraco.Core/Models/PropertyExtensions.cs b/src/Umbraco.Core/Models/PropertyExtensions.cs
deleted file mode 100644
index dff10f0c03..0000000000
--- a/src/Umbraco.Core/Models/PropertyExtensions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using System.Xml;
-using System.Xml.Linq;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Composing;
-using Umbraco.Core.PropertyEditors;
-using Umbraco.Core.Services;
-
-namespace Umbraco.Core.Models
-{
- public static class PropertyExtensions
- {
- ///
- /// Creates the xml representation for the object
- ///
- /// to generate xml for
- /// Xml of the property and its value
- public static XElement ToXml(this Property property)
- {
- var xmlSerializer = new EntityXmlSerializer();
- return xmlSerializer.Serialize(Current.Services.DataTypeService, property);
- }
-
- }
-}
diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs
index 76e0e98146..fbec228201 100644
--- a/src/Umbraco.Core/Models/PropertyGroup.cs
+++ b/src/Umbraco.Core/Models/PropertyGroup.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Specialized;
using System.Diagnostics;
-using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
@@ -16,21 +15,22 @@ namespace Umbraco.Core.Models
[DebuggerDisplay("Id: {Id}, Name: {Name}")]
public class PropertyGroup : Entity, IEquatable
{
+ private static readonly Lazy Ps = new Lazy();
+
private string _name;
private int _sortOrder;
private PropertyTypeCollection _propertyTypes;
- public PropertyGroup() : this(new PropertyTypeCollection())
- {
- }
+ public PropertyGroup(bool isPublishing)
+ : this(new PropertyTypeCollection(isPublishing))
+ { }
public PropertyGroup(PropertyTypeCollection propertyTypeCollection)
{
PropertyTypes = propertyTypeCollection;
}
- private static readonly Lazy Ps = new Lazy();
-
+ // ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
@@ -38,7 +38,7 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyTypes);
}
- void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e)
+ private void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(Ps.Value.PropertyTypeCollectionSelector);
}
@@ -49,8 +49,8 @@ namespace Umbraco.Core.Models
[DataMember]
public string Name
{
- get { return _name; }
- set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); }
+ get => _name;
+ set => SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector);
}
///
@@ -59,8 +59,8 @@ namespace Umbraco.Core.Models
[DataMember]
public int SortOrder
{
- get { return _sortOrder; }
- set { SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector); }
+ get => _sortOrder;
+ set => SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector);
}
///
@@ -69,16 +69,15 @@ namespace Umbraco.Core.Models
[DataMember]
public PropertyTypeCollection PropertyTypes
{
- get { return _propertyTypes; }
+ get => _propertyTypes;
set
{
_propertyTypes = value;
- //since we're adding this collection to this group, we need to ensure that all the lazy values are set.
+ // since we're adding this collection to this group,
+ // we need to ensure that all the lazy values are set.
foreach (var propertyType in _propertyTypes)
- {
- propertyType.PropertyGroupId = new Lazy(() => this.Id);
- }
+ propertyType.PropertyGroupId = new Lazy(() => Id);
_propertyTypes.CollectionChanged += PropertyTypesChanged;
}
@@ -87,22 +86,14 @@ namespace Umbraco.Core.Models
public bool Equals(PropertyGroup other)
{
if (base.Equals(other)) return true;
-
- //Check whether the PropertyGroup's properties are equal.
- return Name.InvariantEquals(other.Name);
+ return other != null && Name.InvariantEquals(other.Name);
}
public override int GetHashCode()
{
- //Get hash code for the Name field if it is not null.
- int baseHash = base.GetHashCode();
-
- //Get hash code for the Alias field.
- int nameHash = Name.ToLowerInvariant().GetHashCode();
-
- //Calculate the hash code for the product.
+ var baseHash = base.GetHashCode();
+ var nameHash = Name.ToLowerInvariant().GetHashCode();
return baseHash ^ nameHash;
}
-
}
}
diff --git a/src/Umbraco.Core/Models/PropertyGroupCollection.cs b/src/Umbraco.Core/Models/PropertyGroupCollection.cs
index 7530208ada..fda40644f4 100644
--- a/src/Umbraco.Core/Models/PropertyGroupCollection.cs
+++ b/src/Umbraco.Core/Models/PropertyGroupCollection.cs
@@ -21,9 +21,7 @@ namespace Umbraco.Core.Models
internal Action OnAdd;
internal PropertyGroupCollection()
- {
-
- }
+ { }
public PropertyGroupCollection(IEnumerable groups)
{
@@ -38,7 +36,8 @@ namespace Umbraco.Core.Models
internal void Reset(IEnumerable groups)
{
Clear();
- groups.ForEach(Add);
+ foreach (var group in groups)
+ Add(group);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
@@ -74,12 +73,12 @@ namespace Umbraco.Core.Models
//Note this is done to ensure existig groups can be renamed
if (item.HasIdentity && item.Id > 0)
{
- var exists = this.Contains(item.Id);
+ var exists = Contains(item.Id);
if (exists)
{
- var keyExists = this.Contains(item.Name);
- if(keyExists)
- throw new Exception(string.Format("Naming conflict: Changing the name of PropertyGroup '{0}' would result in duplicates", item.Name));
+ var keyExists = Contains(item.Name);
+ if (keyExists)
+ throw new Exception($"Naming conflict: Changing the name of PropertyGroup '{item.Name}' would result in duplicates");
SetItem(IndexOfKey(item.Id), item);
return;
@@ -90,7 +89,7 @@ namespace Umbraco.Core.Models
var key = GetKeyForItem(item);
if (key != null)
{
- var exists = this.Contains(key);
+ var exists = Contains(key);
if (exists)
{
SetItem(IndexOfKey(key), item);
@@ -100,7 +99,7 @@ namespace Umbraco.Core.Models
}
base.Add(item);
- OnAdd.IfNotNull(x => x.Invoke());//Could this not be replaced by a Mandate/Contract for ensuring item is not null
+ OnAdd?.Invoke();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
@@ -132,25 +131,17 @@ namespace Umbraco.Core.Models
public int IndexOfKey(string key)
{
- for (var i = 0; i < this.Count; i++)
- {
+ for (var i = 0; i < Count; i++)
if (this[i].Name == key)
- {
return i;
- }
- }
return -1;
}
public int IndexOfKey(int id)
{
- for (var i = 0; i < this.Count; i++)
- {
+ for (var i = 0; i < Count; i++)
if (this[i].Id == id)
- {
return i;
- }
- }
return -1;
}
@@ -163,20 +154,17 @@ namespace Umbraco.Core.Models
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
- if (CollectionChanged != null)
- {
- CollectionChanged(this, args);
- }
+ CollectionChanged?.Invoke(this, args);
}
public object DeepClone()
{
- var newGroup = new PropertyGroupCollection();
- foreach (var p in this)
+ var clone = new PropertyGroupCollection();
+ foreach (var group in this)
{
- newGroup.Add((PropertyGroup)p.DeepClone());
+ clone.Add((PropertyGroup) group.DeepClone());
}
- return newGroup;
+ return clone;
}
}
}
diff --git a/src/Umbraco.Core/Models/PropertyTagBehavior.cs b/src/Umbraco.Core/Models/PropertyTagBehavior.cs
deleted file mode 100644
index f0a6d47fb5..0000000000
--- a/src/Umbraco.Core/Models/PropertyTagBehavior.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Umbraco.Core.Models
-{
- internal enum PropertyTagBehavior
- {
- Replace,
- Remove,
- Merge
- }
-}
diff --git a/src/Umbraco.Core/Models/PropertyTagChange.cs b/src/Umbraco.Core/Models/PropertyTagChange.cs
new file mode 100644
index 0000000000..bb8158b61d
--- /dev/null
+++ b/src/Umbraco.Core/Models/PropertyTagChange.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// A set of tag changes.
+ ///
+ internal class PropertyTagChange
+ {
+ public ChangeType Type { get; set; }
+
+ public IEnumerable> Tags { get; set; }
+
+ public enum ChangeType
+ {
+ Replace,
+ Remove,
+ Merge
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Models/PropertyTags.cs b/src/Umbraco.Core/Models/PropertyTags.cs
deleted file mode 100644
index 7af66401af..0000000000
--- a/src/Umbraco.Core/Models/PropertyTags.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Umbraco.Core.Models
-{
- ///
- /// A property extension class that allows us to enable tags for any given property
- ///
- internal class PropertyTags
- {
- public PropertyTags()
- {
- Enable = false;
- Behavior = PropertyTagBehavior.Merge;
- }
-
- ///
- /// The behavior of how to save the tags assigned -
- /// Merge (keep existing and append new),
- /// Remove (remove any of the tags in the Tags property that are currently assigned,
- /// Replace (replace the currently assigned tags with the ones specified)
- ///
- public PropertyTagBehavior Behavior { get; set; }
-
- ///
- /// Flags the property to have tagging enabled
- ///
- public bool Enable { get; set; }
-
- ///
- /// The actual tags to associate - tag/group
- ///
- public IEnumerable> Tags { get; set; }
-
- }
-}
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index 42b8ad839b..b169e5841d 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -1,6 +1,5 @@
using System;
using System.Diagnostics;
-using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
@@ -19,6 +18,8 @@ namespace Umbraco.Core.Models
[DebuggerDisplay("Id: {Id}, Name: {Name}, Alias: {Alias}")]
public class PropertyType : Entity, IEquatable
{
+ private static readonly Lazy Ps = new Lazy();
+
private readonly bool _isExplicitDbType;
private string _name;
private string _alias;
@@ -51,13 +52,11 @@ namespace Umbraco.Core.Models
public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType)
: this(propertyEditorAlias, dataTypeDatabaseType, false)
- {
- }
+ { }
public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType, string propertyTypeAlias)
: this(propertyEditorAlias, dataTypeDatabaseType, false, propertyTypeAlias)
- {
- }
+ { }
///
/// Used internally to assign an explicity database type for this property type regardless of what the underlying data type/property editor is.
@@ -87,8 +86,7 @@ namespace Umbraco.Core.Models
_alias = GetAlias(propertyTypeAlias);
}
- private static readonly Lazy Ps = new Lazy();
-
+ // ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
@@ -104,14 +102,16 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo PropertyGroupIdSelector = ExpressionHelper.GetPropertyInfo>(x => x.PropertyGroupId);
}
+ public bool IsPublishing { get; internal set; }
+
///
/// Gets of Sets the Name of the PropertyType
///
[DataMember]
public string Name
{
- get { return _name; }
- set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); }
+ get => _name;
+ set => SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector);
}
///
@@ -120,8 +120,8 @@ namespace Umbraco.Core.Models
[DataMember]
public string Alias
{
- get { return _alias; }
- set { SetPropertyValueAndDetectChanges(GetAlias(value), ref _alias, Ps.Value.AliasSelector); }
+ get => _alias;
+ set => SetPropertyValueAndDetectChanges(GetAlias(value), ref _alias, Ps.Value.AliasSelector);
}
///
@@ -130,8 +130,8 @@ namespace Umbraco.Core.Models
[DataMember]
public string Description
{
- get { return _description; }
- set { SetPropertyValueAndDetectChanges(value, ref _description, Ps.Value.DescriptionSelector); }
+ get => _description;
+ set => SetPropertyValueAndDetectChanges(value, ref _description, Ps.Value.DescriptionSelector);
}
///
@@ -141,15 +141,15 @@ namespace Umbraco.Core.Models
[DataMember]
public int DataTypeDefinitionId
{
- get { return _dataTypeDefinitionId; }
- set { SetPropertyValueAndDetectChanges(value, ref _dataTypeDefinitionId, Ps.Value.DataTypeDefinitionIdSelector); }
+ get => _dataTypeDefinitionId;
+ set => SetPropertyValueAndDetectChanges(value, ref _dataTypeDefinitionId, Ps.Value.DataTypeDefinitionIdSelector);
}
[DataMember]
public string PropertyEditorAlias
{
- get { return _propertyEditorAlias; }
- set { SetPropertyValueAndDetectChanges(value, ref _propertyEditorAlias, Ps.Value.PropertyEditorAliasSelector); }
+ get => _propertyEditorAlias;
+ set => SetPropertyValueAndDetectChanges(value, ref _propertyEditorAlias, Ps.Value.PropertyEditorAliasSelector);
}
///
@@ -159,11 +159,7 @@ namespace Umbraco.Core.Models
[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 DataTypeId
{
- get
- {
- return LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(
- _propertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId).Value;
- }
+ get => LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(_propertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId).Value;
set
{
var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(value, true);
@@ -177,12 +173,11 @@ namespace Umbraco.Core.Models
[DataMember]
internal DataTypeDatabaseType DataTypeDatabaseType
{
- get { return _dataTypeDatabaseType; }
+ get => _dataTypeDatabaseType;
set
{
//don't allow setting this if an explicit declaration has been made in the ctor
if (_isExplicitDbType) return;
-
SetPropertyValueAndDetectChanges(value, ref _dataTypeDatabaseType, Ps.Value.DataTypeDatabaseTypeSelector);
}
}
@@ -194,8 +189,8 @@ namespace Umbraco.Core.Models
[DataMember]
internal Lazy PropertyGroupId
{
- get { return _propertyGroupId; }
- set { SetPropertyValueAndDetectChanges(value, ref _propertyGroupId, Ps.Value.PropertyGroupIdSelector); }
+ get => _propertyGroupId;
+ set => SetPropertyValueAndDetectChanges(value, ref _propertyGroupId, Ps.Value.PropertyGroupIdSelector);
}
///
@@ -204,8 +199,8 @@ namespace Umbraco.Core.Models
[DataMember]
public bool Mandatory
{
- get { return _mandatory; }
- set { SetPropertyValueAndDetectChanges(value, ref _mandatory, Ps.Value.MandatorySelector); }
+ get => _mandatory;
+ set => SetPropertyValueAndDetectChanges(value, ref _mandatory, Ps.Value.MandatorySelector);
}
///
@@ -215,8 +210,8 @@ namespace Umbraco.Core.Models
[Obsolete("Not used anywhere, will be removed in future versions")]
public string HelpText
{
- get { return _helpText; }
- set { SetPropertyValueAndDetectChanges(value, ref _helpText, Ps.Value.HelpTextSelector); }
+ get => _helpText;
+ set => SetPropertyValueAndDetectChanges(value, ref _helpText, Ps.Value.HelpTextSelector);
}
///
@@ -225,8 +220,8 @@ namespace Umbraco.Core.Models
[DataMember]
public int SortOrder
{
- get { return _sortOrder; }
- set { SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector); }
+ get => _sortOrder;
+ set => SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector);
}
///
@@ -235,11 +230,11 @@ namespace Umbraco.Core.Models
[DataMember]
public string ValidationRegExp
{
- get { return _validationRegExp; }
- set { SetPropertyValueAndDetectChanges(value, ref _validationRegExp, Ps.Value.ValidationRegExpSelector); }
+ get => _validationRegExp;
+ set => SetPropertyValueAndDetectChanges(value, ref _validationRegExp, Ps.Value.ValidationRegExpSelector);
}
- private string GetAlias(string value)
+ private static string GetAlias(string value)
{
//NOTE: WE are doing this because we don't want to do a ToSafeAlias when the alias is the special case of
// being prefixed with Constants.PropertyEditors.InternalGenericPropertiesPrefix
@@ -339,7 +334,7 @@ namespace Umbraco.Core.Models
}
catch
{
- throw new Exception(string .Format("Invalid validation expression on property {0}",this.Alias));
+ throw new Exception($"Invalid validation expression on property {Alias}");
}
}
diff --git a/src/Umbraco.Core/Models/PropertyTypeCollection.cs b/src/Umbraco.Core/Models/PropertyTypeCollection.cs
index d0962ca9c4..4788221854 100644
--- a/src/Umbraco.Core/Models/PropertyTypeCollection.cs
+++ b/src/Umbraco.Core/Models/PropertyTypeCollection.cs
@@ -5,12 +5,11 @@ using System.Collections.Specialized;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
-using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
///
- /// Represents a collection of objects
+ /// Represents a collection of objects.
///
[Serializable]
[DataContract]
@@ -22,16 +21,19 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
internal Action OnAdd;
- internal PropertyTypeCollection()
+ internal PropertyTypeCollection(bool isPublishing)
{
-
+ IsPublishing = isPublishing;
}
- public PropertyTypeCollection(IEnumerable properties)
+ public PropertyTypeCollection(bool isPublishing, IEnumerable properties)
+ : this(isPublishing)
{
Reset(properties);
}
+ public bool IsPublishing { get; }
+
///
/// Resets the collection to only contain the instances referenced in the parameter.
///
@@ -40,12 +42,14 @@ namespace Umbraco.Core.Models
internal void Reset(IEnumerable properties)
{
Clear();
- properties.ForEach(Add);
+ foreach (var property in properties)
+ Add(property);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void SetItem(int index, PropertyType item)
{
+ item.IsPublishing = IsPublishing;
base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
@@ -59,6 +63,7 @@ namespace Umbraco.Core.Models
protected override void InsertItem(int index, PropertyType item)
{
+ item.IsPublishing = IsPublishing;
base.InsertItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
@@ -72,12 +77,15 @@ namespace Umbraco.Core.Models
//TODO: Instead of 'new' this should explicitly implement one of the collection interfaces members
internal new void Add(PropertyType item)
{
+ item.IsPublishing = IsPublishing;
+
+ // fixme redo this entirely!!!
using (new WriteLock(_addLocker))
{
var key = GetKeyForItem(item);
if (key != null)
{
- var exists = this.Contains(key);
+ var exists = Contains(key);
if (exists)
{
SetItem(IndexOfKey(key), item);
@@ -93,7 +101,7 @@ namespace Umbraco.Core.Models
}
base.Add(item);
- OnAdd.IfNotNull(x => x.Invoke());//Could this not be replaced by a Mandate/Contract for ensuring item is not null
+ OnAdd?.Invoke();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
@@ -113,20 +121,14 @@ namespace Umbraco.Core.Models
public void RemoveItem(string propertyTypeAlias)
{
var key = IndexOfKey(propertyTypeAlias);
- //Only removes an item if the key was found
- if(key != -1)
- RemoveItem(key);
+ if (key != -1) RemoveItem(key);
}
public int IndexOfKey(string key)
{
- for (var i = 0; i < this.Count; i++)
- {
+ for (var i = 0; i < Count; i++)
if (this[i].Alias == key)
- {
return i;
- }
- }
return -1;
}
@@ -139,20 +141,15 @@ namespace Umbraco.Core.Models
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
- if (CollectionChanged != null)
- {
- CollectionChanged(this, args);
- }
+ CollectionChanged?.Invoke(this, args);
}
public object DeepClone()
{
- var newGroup = new PropertyTypeCollection();
- foreach (var p in this)
- {
- newGroup.Add((PropertyType)p.DeepClone());
- }
- return newGroup;
+ var clone = new PropertyTypeCollection(IsPublishing);
+ foreach (var propertyType in this)
+ clone.Add((PropertyType) propertyType.DeepClone());
+ return clone;
}
}
}
diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs
index 8ddcebd1ff..e359e690b8 100644
--- a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs
@@ -20,6 +20,9 @@ namespace Umbraco.Core.Models.Rdbms
[Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Published")]
public bool Published { get; set; }
+ [Column("edited")]
+ public bool Edited { get; set; }
+
[Column("releaseDate")]
[NullSetting(NullSetting = NullSettings.Null)]
public DateTime? ReleaseDate { get; set; }
@@ -28,6 +31,22 @@ namespace Umbraco.Core.Models.Rdbms
[NullSetting(NullSetting = NullSettings.Null)]
public DateTime? ExpiresDate { get; set; }
+ [Column("publishDate")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public DateTime? PublishDate { get; set; }
+
+ [Column("publishUserId")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public int? PublishUserId { get; set; }
+
+ [Column("publishName")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public string PublishName { get; set; }
+
+ [Column("publishTemplateId")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public int? PublishTemplateId { get; set; }
+
[ResultColumn]
[Reference(ReferenceType.OneToOne, ReferenceMemberName = "NodeId")]
public ContentDto ContentDto { get; set; }
diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs
index 799c09b531..310a3e51d8 100644
--- a/src/Umbraco.Core/Models/UmbracoEntity.cs
+++ b/src/Umbraco.Core/Models/UmbracoEntity.cs
@@ -13,6 +13,8 @@ namespace Umbraco.Core.Models
///
public class UmbracoEntity : Entity, IUmbracoEntity
{
+ private static readonly Lazy Ps = new Lazy();
+
private int _creatorId;
private int _level;
private string _name;
@@ -21,14 +23,12 @@ namespace Umbraco.Core.Models
private int _sortOrder;
private bool _trashed;
private bool _hasChildren;
- private bool _isPublished;
- private bool _isDraft;
- private bool _hasPendingChanges;
+ private bool _published;
+ private bool _edited;
private string _contentTypeAlias;
private Guid _nodeObjectTypeId;
- private static readonly Lazy Ps = new Lazy();
-
+ // ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => x.CreatorId);
@@ -39,9 +39,8 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder);
public readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo(x => x.Trashed);
public readonly PropertyInfo HasChildrenSelector = ExpressionHelper.GetPropertyInfo(x => x.HasChildren);
- public readonly PropertyInfo IsPublishedSelector = ExpressionHelper.GetPropertyInfo(x => x.IsPublished);
- public readonly PropertyInfo IsDraftSelector = ExpressionHelper.GetPropertyInfo(x => x.IsDraft);
- public readonly PropertyInfo HasPendingChangesSelector = ExpressionHelper.GetPropertyInfo(x => x.HasPendingChanges);
+ public readonly PropertyInfo PublishedSelector = ExpressionHelper.GetPropertyInfo(x => x.Published);
+ public readonly PropertyInfo EditedSelector = ExpressionHelper.GetPropertyInfo(x => x.Edited);
public readonly PropertyInfo ContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeAlias);
public readonly PropertyInfo ContentTypeIconSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeIcon);
public readonly PropertyInfo ContentTypeThumbnailSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeThumbnail);
@@ -73,154 +72,135 @@ namespace Umbraco.Core.Models
public int CreatorId
{
- get { return _creatorId; }
- set { SetPropertyValueAndDetectChanges(value, ref _creatorId, Ps.Value.CreatorIdSelector); }
+ get => _creatorId;
+ set => SetPropertyValueAndDetectChanges(value, ref _creatorId, Ps.Value.CreatorIdSelector);
}
public int Level
{
- get { return _level; }
- set { SetPropertyValueAndDetectChanges(value, ref _level, Ps.Value.LevelSelector); }
+ get => _level;
+ set => SetPropertyValueAndDetectChanges(value, ref _level, Ps.Value.LevelSelector);
}
public string Name
{
- get { return _name; }
- set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); }
+ get => _name;
+ set => SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector);
}
public int ParentId
{
- get { return _parentId; }
- set { SetPropertyValueAndDetectChanges(value, ref _parentId, Ps.Value.ParentIdSelector); }
+ get => _parentId;
+ set => SetPropertyValueAndDetectChanges(value, ref _parentId, Ps.Value.ParentIdSelector);
}
public string Path
{
- get { return _path; }
- set { SetPropertyValueAndDetectChanges(value, ref _path, Ps.Value.PathSelector); }
+ get => _path;
+ set => SetPropertyValueAndDetectChanges(value, ref _path, Ps.Value.PathSelector);
}
public int SortOrder
{
- get { return _sortOrder; }
- set { SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector); }
+ get => _sortOrder;
+ set => SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector);
}
public bool Trashed
{
- get { return _trashed; }
- private set { SetPropertyValueAndDetectChanges(value, ref _trashed, Ps.Value.TrashedSelector); }
+ get => _trashed;
+ private set => SetPropertyValueAndDetectChanges(value, ref _trashed, Ps.Value.TrashedSelector);
}
- public IDictionary AdditionalData { get; private set; }
+ public IDictionary AdditionalData { get; }
public bool HasChildren
{
- get { return _hasChildren; }
+ get => _hasChildren;
set
{
SetPropertyValueAndDetectChanges(value, ref _hasChildren, Ps.Value.HasChildrenSelector);
- //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
- AdditionalData["HasChildren"] = value;
+ AdditionalData["HasChildren"] = value; // custom and not in IUmbracoEntity
}
}
- public bool IsPublished
+ public bool Published
{
- get { return _isPublished; }
+ get => _published;
set
{
- SetPropertyValueAndDetectChanges(value, ref _isPublished, Ps.Value.IsPublishedSelector);
- //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
- AdditionalData["IsPublished"] = value;
+ SetPropertyValueAndDetectChanges(value, ref _published, Ps.Value.PublishedSelector);
+ AdditionalData["IsPublished"] = value; // custom and not in IUmbracoEntity
}
}
- public bool IsDraft
+ public bool Edited
{
- get { return _isDraft; }
+ get => _edited;
set
{
- SetPropertyValueAndDetectChanges(value, ref _isDraft, Ps.Value.IsDraftSelector);
- //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
- AdditionalData["IsDraft"] = value;
- }
- }
-
- public bool HasPendingChanges
- {
- get { return _hasPendingChanges; }
- set
- {
- SetPropertyValueAndDetectChanges(value, ref _hasPendingChanges, Ps.Value.HasPendingChangesSelector);
- //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
- AdditionalData["HasPendingChanges"] = value;
+ SetPropertyValueAndDetectChanges(value, ref _edited, Ps.Value.EditedSelector);
+ AdditionalData["IsEdited"] = value; // custom and not in IUmbracoEntity
}
}
public string ContentTypeAlias
{
- get { return _contentTypeAlias; }
+ get => _contentTypeAlias;
set
{
SetPropertyValueAndDetectChanges(value, ref _contentTypeAlias, Ps.Value.ContentTypeAliasSelector);
- //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
- AdditionalData["ContentTypeAlias"] = value;
+ AdditionalData["ContentTypeAlias"] = value; // custom and not in IUmbracoEntity
}
}
public string ContentTypeIcon
{
- get { return _contentTypeIcon; }
+ get => _contentTypeIcon;
set
{
SetPropertyValueAndDetectChanges(value, ref _contentTypeIcon, Ps.Value.ContentTypeIconSelector);
- //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
- AdditionalData["ContentTypeIcon"] = value;
+ AdditionalData["ContentTypeIcon"] = value; // custom and not in IUmbracoEntity
}
}
public string ContentTypeThumbnail
{
- get { return _contentTypeThumbnail; }
+ get => _contentTypeThumbnail;
set
{
SetPropertyValueAndDetectChanges(value, ref _contentTypeThumbnail, Ps.Value.ContentTypeThumbnailSelector);
- //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
- AdditionalData["ContentTypeThumbnail"] = value;
+ AdditionalData["ContentTypeThumbnail"] = value; // custom and not in IUmbracoEntity
}
}
public Guid NodeObjectTypeId
{
- get { return _nodeObjectTypeId; }
+ get => _nodeObjectTypeId;
set
{
SetPropertyValueAndDetectChanges(value, ref _nodeObjectTypeId, Ps.Value.NodeObjectTypeIdSelector);
- //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
- AdditionalData["NodeObjectTypeId"] = value;
+ AdditionalData["NodeObjectTypeId"] = value; // custom and not in IUmbracoEntity
}
}
public override object DeepClone()
{
var clone = (UmbracoEntity) base.DeepClone();
- //turn off change tracking
+
+ // turn off change tracking
clone.DisableChangeTracking();
- //This ensures that any value in the dictionary that is deep cloneable is cloned too
+
+ // ensure that any value in the dictionary that is deep cloneable is cloned too
foreach (var key in clone.AdditionalData.Keys.ToArray())
{
- var deepCloneable = clone.AdditionalData[key] as IDeepCloneable;
- if (deepCloneable != null)
- {
+ if (clone.AdditionalData[key] is IDeepCloneable deepCloneable)
clone.AdditionalData[key] = deepCloneable.DeepClone();
- }
}
- //this shouldn't really be needed since we're not tracking
- clone.ResetDirtyProperties(false);
- //re-enable tracking
+
+ // re-enable tracking
+ clone.ResetDirtyProperties(false); // why? were not tracking
clone.EnableChangeTracking();
return clone;
}
@@ -233,6 +213,7 @@ namespace Umbraco.Core.Models
{
public string PropertyEditorAlias { get; set; }
public object Value { get; set; }
+
public object DeepClone()
{
//Memberwise clone on Entity will work since it doesn't have any deep elements
@@ -243,14 +224,14 @@ namespace Umbraco.Core.Models
protected bool Equals(EntityProperty other)
{
- return PropertyEditorAlias.Equals(other.PropertyEditorAlias) && string.Equals(Value, other.Value);
+ 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() != this.GetType()) return false;
+ if (obj.GetType() != GetType()) return false;
return Equals((EntityProperty) obj);
}
diff --git a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs
index 2368cb3e07..241b5c6ee6 100644
--- a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs
@@ -41,11 +41,16 @@ namespace Umbraco.Core.Persistence.Factories
content.UpdateDate = contentDto.UpdateDate;
content.Published = dto.Published;
+ content.Edited = dto.Edited;
content.ExpireDate = dto.ExpiresDate;
content.ReleaseDate = dto.ReleaseDate;
- // if not published, published date has no meaning really
- content.PublishedDate = dto.Published ? contentVersionDto.VersionDate : DateTime.MinValue;
+ if (dto.Published)
+ {
+ content.PublishDate = dto.PublishDate;
+ content.PublishName = dto.PublishName;
+ content.PublisherId = dto.PublishUserId;
+ }
// reset dirty initial properties (U4-1946)
content.ResetDirtyProperties(false);
@@ -60,9 +65,9 @@ namespace Umbraco.Core.Persistence.Factories
///
/// Buils a dto from an IContent item.
///
- public static DocumentDto BuildDto(IContent entity)
+ public static DocumentDto BuildDto(IContent entity, Guid objectType)
{
- var contentDto = BuildContentDto(entity);
+ var contentDto = BuildContentDto(entity, objectType);
var dto = new DocumentDto
{
@@ -78,7 +83,7 @@ namespace Umbraco.Core.Persistence.Factories
return dto;
}
- private static ContentDto BuildContentDto(IContent entity)
+ private static ContentDto BuildContentDto(IContent entity, Guid objectType)
{
var dto = new ContentDto
{
@@ -87,13 +92,13 @@ namespace Umbraco.Core.Persistence.Factories
WriterUserId = entity.WriterId,
UpdateDate = entity.UpdateDate,
- NodeDto = BuildNodeDto(entity)
+ NodeDto = BuildNodeDto(entity, objectType)
};
return dto;
}
- private static NodeDto BuildNodeDto(IContent entity)
+ private static NodeDto BuildNodeDto(IContent entity, Guid objectType)
{
var dto = new NodeDto
{
@@ -106,7 +111,7 @@ namespace Umbraco.Core.Persistence.Factories
Trashed = entity.Trashed,
UserId = entity.CreatorId,
Text = entity.Name,
- NodeObjectType = Constants.ObjectTypes.Document,
+ NodeObjectType = objectType,
CreateDate = entity.CreateDate
};
diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
index 203b9bc919..00dc1a83ce 100644
--- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
@@ -78,7 +78,7 @@ namespace Umbraco.Core.Persistence.Factories
var propertyGroups = new PropertyGroupCollection();
foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue))
{
- var group = new PropertyGroup();
+ var group = new PropertyGroup(MemberType.IsPublishingConst);
// if the group is defined on the current member type,
// assign its identifier, else it will be zero
@@ -93,7 +93,7 @@ namespace Umbraco.Core.Persistence.Factories
group.Key = groupDto.UniqueId;
group.Name = groupDto.Text;
group.SortOrder = groupDto.SortOrder;
- group.PropertyTypes = new PropertyTypeCollection();
+ group.PropertyTypes = new PropertyTypeCollection(MemberType.IsPublishingConst);
//Because we are likely to have a group with no PropertyTypes we need to ensure that these are excluded
var localGroupDto = groupDto;
diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
index 31f034830a..0042065ed2 100644
--- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
@@ -11,6 +11,7 @@ namespace Umbraco.Core.Persistence.Factories
public static IEnumerable BuildEntities(IReadOnlyCollection dtos, PropertyType[] propertyTypes)
{
var properties = new List();
+ var propsDtos = dtos.GroupBy(x => x.PropertyTypeId).ToDictionary(x => x.Key, x => (IEnumerable) x);
foreach (var propertyType in propertyTypes)
{
@@ -20,9 +21,13 @@ namespace Umbraco.Core.Persistence.Factories
{
property.DisableChangeTracking();
- var propDtos = dtos.Where(x => x.PropertyTypeId == propertyType.Id);
- foreach (var propDto in propDtos)
- property.SetValue(propDto.LanguageId, propDto.Segment, propDto.Value);
+ // see notes in BuildDtos - we always have edit+published dtos
+
+ if (propsDtos.TryGetValue(propertyType.Id, out var propDtos))
+ {
+ foreach (var propDto in propDtos)
+ property.FactorySetValue(propDto.LanguageId, propDto.Segment, propDto.Published, propDto.Value);
+ }
property.ResetDirtyProperties(false);
properties.Add(property);
@@ -36,7 +41,7 @@ namespace Umbraco.Core.Persistence.Factories
return properties;
}
- private static PropertyDataDto BuildDto(int nodeId, Guid versionId, Property property, Property.PropertyValue propertyValue, bool published)
+ private static PropertyDataDto BuildDto(int nodeId, Guid versionId, Property property, Property.PropertyValue propertyValue, bool published, object value)
{
var dto = new PropertyDataDto { NodeId = nodeId, VersionId = versionId, PropertyTypeId = property.PropertyTypeId };
@@ -48,8 +53,6 @@ namespace Umbraco.Core.Persistence.Factories
dto.Published = published;
- var value = published ? propertyValue.PublishedValue : propertyValue.DraftValue;
-
if (property.DataTypeDatabaseType == DataTypeDatabaseType.Integer)
{
if (value is bool || property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.TrueFalseAlias)
@@ -87,18 +90,42 @@ namespace Umbraco.Core.Persistence.Factories
return dto;
}
- public static IEnumerable BuildDtos(int nodeId, Guid versionId, IEnumerable properties)
+ public static IEnumerable BuildDtos(int nodeId, Guid versionId, IEnumerable properties, out bool edited)
{
var propertyDataDtos = new List();
+ edited = false;
foreach (var property in properties)
{
- foreach (var propertyValue in property.Values)
+ if (property.PropertyType.IsPublishing)
{
- if (propertyValue.DraftValue != null)
- propertyDataDtos.Add(BuildDto(nodeId, versionId, property, propertyValue, false));
- if (propertyValue.PublishedValue != null)
- propertyDataDtos.Add(BuildDto(nodeId, versionId, property, propertyValue, true));
+ // publishing = deal with edit and published values
+ foreach (var propertyValue in property.Values)
+ {
+ // create a dto for both edit and published - make sure we have one for edit
+ // we *could* think of optimizing by creating a dto for edit only if EditValue != PublishedValue
+ // but then queries in db would be way more complicated and require coalescing between edit and
+ // published dtos - not worth it
+ if (propertyValue.PublishedValue != null)
+ propertyDataDtos.Add(BuildDto(nodeId, versionId, property, propertyValue, true, propertyValue.PublishedValue));
+ if (propertyValue.EditValue != null)
+ propertyDataDtos.Add(BuildDto(nodeId, versionId, property, propertyValue, false, propertyValue.EditValue));
+ else if (propertyValue.PublishedValue != null)
+ propertyDataDtos.Add(BuildDto(nodeId, versionId, property, propertyValue, false, propertyValue.PublishedValue));
+
+ // use explicit equals here, else object comparison fails at comparing eg strings
+ var sameValues = propertyValue.PublishedValue == null ? propertyValue.EditValue == null : propertyValue.PublishedValue.Equals(propertyValue.EditValue);
+ edited |= !sameValues;
+ }
+ }
+ else
+ {
+ foreach (var propertyValue in property.Values)
+ {
+ // not publishing = only deal with edit values
+ if (propertyValue.EditValue != null)
+ propertyDataDtos.Add(BuildDto(nodeId, versionId, property, propertyValue, false, propertyValue.EditValue));
+ }
}
}
diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
index 07f82d4354..cf95dc987d 100644
--- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
@@ -30,14 +30,14 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory,IEnumerable>
- public IEnumerable BuildEntity(IEnumerable groupDtos)
+ public IEnumerable BuildEntity(IEnumerable groupDtos, bool isPublishing)
{
// groupDtos contains all the groups, those that are defined on the current
// content type, and those that are inherited from composition content types
var propertyGroups = new PropertyGroupCollection();
foreach (var groupDto in groupDtos)
{
- var group = new PropertyGroup();
+ var group = new PropertyGroup(isPublishing);
try
{
@@ -50,7 +50,7 @@ namespace Umbraco.Core.Persistence.Factories
group.Name = groupDto.Text;
group.SortOrder = groupDto.SortOrder;
- group.PropertyTypes = new PropertyTypeCollection();
+ group.PropertyTypes = new PropertyTypeCollection(isPublishing);
group.Key = groupDto.UniqueId;
//Because we are likely to have a group with no PropertyTypes we need to ensure that these are excluded
@@ -142,7 +142,7 @@ namespace Umbraco.Core.Persistence.Factories
UniqueId = propertyType.Key
};
- if (tabId != default(int))
+ if (tabId != default)
{
propertyTypeDto.PropertyTypeGroupId = tabId;
}
diff --git a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs
index 5a1befa875..b7d3391318 100644
--- a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs
@@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
-using System.Reflection;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
-using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Persistence.Factories
@@ -27,8 +24,7 @@ namespace Umbraco.Core.Persistence.Factories
internal UmbracoEntity BuildEntityFromDynamic(dynamic d)
{
- var asDictionary = (IDictionary)d;
-
+ var asDictionary = (IDictionary) d;
var entity = new UmbracoEntity(d.trashed);
try
@@ -49,24 +45,12 @@ namespace Umbraco.Core.Persistence.Factories
entity.ContentTypeAlias = asDictionary.ContainsKey("alias") ? (d.alias ?? string.Empty) : string.Empty;
entity.ContentTypeIcon = asDictionary.ContainsKey("icon") ? (d.icon ?? string.Empty) : string.Empty;
entity.ContentTypeThumbnail = asDictionary.ContainsKey("thumbnail") ? (d.thumbnail ?? string.Empty) : string.Empty;
+ //entity.VersionId = asDictionary.ContainsKey("versionId") ? asDictionary["versionId"] : Guid.Empty;
- var publishedVersion = default(Guid);
- //some content items don't have a published/newest version
- if (asDictionary.ContainsKey("publishedVersion") && asDictionary["publishedVersion"] != null)
- {
- Guid.TryParse(d.publishedVersion.ToString(), out publishedVersion);
- }
- var newestVersion = default(Guid);
- if (asDictionary.ContainsKey("newestVersion") && d.newestVersion != null)
- {
- Guid.TryParse(d.newestVersion.ToString(), out newestVersion);
- }
+ entity.Published = asDictionary.ContainsKey("published") && (bool) asDictionary["published"];
+ entity.Edited = asDictionary.ContainsKey("edits") && (bool) asDictionary["edits"];
- entity.IsPublished = publishedVersion != default(Guid) || (newestVersion != default(Guid) && publishedVersion == newestVersion);
- entity.IsDraft = newestVersion != default(Guid) && (publishedVersion == default(Guid) || publishedVersion != newestVersion);
- entity.HasPendingChanges = (publishedVersion != default(Guid) && newestVersion != default(Guid)) && publishedVersion != newestVersion;
-
- //Now we can assign the additional data!
+ // assign the additional data
AddAdditionalData(entity, asDictionary);
return entity;
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs
index 6b5ff7aa19..6abd82cda1 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs
@@ -178,6 +178,9 @@ JOIN {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc ON doc.versionId=pda
// ie we keep the published version and remove the draft one
Execute.Code(context =>
{
+ // xxx1 is published, non-newest
+ // xxx2 is newest, non-published
+ // we want to move data from 2 to 1
var versions = context.Database.Fetch(@"SELECT
doc1.versionId versionId1, doc1.newest newest1, doc1.published published1,
doc2.versionId versionId2, doc2.newest newest2, doc2.published published2
@@ -188,14 +191,16 @@ WHERE doc1.newest=0 AND doc1.published=1 AND doc2.newest=1 AND doc2.published=0
");
foreach (var version in versions)
{
+ // move property data from 2 to 1, with published=0
context.Database.Execute($@"UPDATE {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.PropertyData)}
-SET versionId='{version.versionId1}', published=1
-WHERE versionId='{version.versionId2}'");
- context.Database.Execute($@"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.PropertyData)}
+SET versionId='{version.versionId1}', published=0
WHERE versionId='{version.versionId2}'");
+ // and then there is no property data anymore for 2
+ // so we can delete the corresp. document table row
context.Database.Execute($@"DELETE FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)}
WHERE versionId='{version.versionId2}'");
- context.Database.Execute($@"UPDATE {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.Document)}
+ // and mark 1 as newest
+ context.Database.Execute($@"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)}
SET newest=1 WHERE versionId='{version.versionId1}'");
}
return string.Empty;
@@ -203,10 +208,9 @@ SET newest=1 WHERE versionId='{version.versionId1}'");
// update content version
Execute.Sql($@"UPDATE {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.ContentVersion)}
-SET current=1
+SET current=doc.newest
FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.ContentVersion)} ver
-JOIN {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc ON ver.versionId=doc.versionId
-WHERE doc.newest=1");
+JOIN {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc ON ver.versionId=doc.versionId");
// keep only one row per document
Execute.Code(context =>
@@ -228,6 +232,42 @@ WHERE nodeId={version.nodeId} AND versionId<>{version.versionId}");
Delete.Column("versionId").FromTable(PreTables.Document); // fixme usage
Delete.Column("newest").FromTable(PreTables.Document); // fixme usage
+ // ensure that every 'published' property data has a corresponding 'non-published' one
+ // but only for the current version
+ Execute.Sql($@"INSERT INTO {Constants.DatabaseSchema.Tables.PropertyData} (nodeId, versionId, propertyTypeId, languageId, segment, published, intValue, decimalValue, dateValue, varcharValue, textValue)
+SELECT p1.nodeId, p1.versionId, p1.propertyTypeId, p1.languageId, p1.segment, 0, p1.intValue, p1.decimalValue, p1.dateValue, p1.varcharValue, p1.textValue
+FROM {Constants.DatabaseSchema.Tables.PropertyData} p1
+JOIN {Constants.DatabaseSchema.Tables.ContentVersion} ON p1.versionId=cver.versionId AND cver.current=1
+WHERE NOT EXIST (
+ SELECT p2.id
+ FROM {Constants.DatabaseSchema.Tables.PropertyData} p2
+ WHERE
+ p1.nodeId=p2.nodeId AND p1.versionId=p2.versionId
+ AND p1.propertyTypeId=p2.propertyTypeId
+ AND p1.lang=p2.lang AND p1.segment=p2.segment
+ AND p2.published=0
+)");
+
+ // create some columns
+ if (!ColumnExists(PreTables.Document, "edits"))
+ {
+ AddColumn(PreTables.Document, "edits", out var notNull);
+ Execute.Sql($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edits=0");
+ Execute.Sql(notNull);
+
+ // set 'edits' to true whenever a 'non-published' property data is != a published one
+ Execute.Sql($@"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edits=0");
+
+ Execute.Sql($@"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edits=1 WHERE nodeId IN (
+SELECT p1.nodeId
+FROM {Constants.DatabaseSchema.Tables.PropertyData} p1
+JOIN {Constants.DatabaseSchema.Tables.ContentVersion} ON p1.versionId=cver.versionId AND cver.current=1
+JOIN {Constants.DatabaseSchema.Tables.PropertyData} p2
+ON p1.nodeId=p2.nodeId AND p1.versionId=p2.versionId AND AND p1.propertyTypeId=p2.propertyTypeId AND p1.lang=p2.lang AND p1.segment=p2.segment AND p2.published=0
+ AND (p1.intValue<>p2.intValue OR p1.decimalValue<>p2.decimalValue OR p1.dateValue<>p2.dateValue OR p1.varcharValue<>p2.varcharValue OR p1.textValue<>p2.textValue)
+WHERE p1.published=1)");
+ }
+
// rename table
Rename.Table(PreTables.Document).To(Constants.DatabaseSchema.Tables.Document);
}
diff --git a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs
index 54e70aca5f..43ffe6f4f7 100644
--- a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs
+++ b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs
@@ -6,6 +6,7 @@ using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using NPoco;
+using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -17,6 +18,12 @@ namespace Umbraco.Core.Persistence
// when doing "sql = sql.Where(...)" actually append to, and return, the original Sql, not
// a new one.
+ #region Alias
+
+ // would love to implement per-column alias in select, where, etc...
+
+ #endregion
+
#region Where
///
@@ -389,7 +396,8 @@ namespace Umbraco.Core.Persistence
{
var sql = new Sql(sqlJoin.SqlContext);
sql = on(sql);
- return sqlJoin.On(sql.SQL, sql.Arguments);
+ var text = sql.SQL.Trim().TrimStart("WHERE").Trim();
+ return sqlJoin.On(text, sql.Arguments);
}
///
@@ -477,6 +485,47 @@ namespace Umbraco.Core.Persistence
return sql.Select(sql.GetColumns(columnExpressions: fields));
}
+ ///
+ /// Creates a SELECT Sql statement for an aliased column.
+ ///
+ /// The type of the DTO to select.
+ /// The origin sql.
+ /// Expression indicating the column to select.
+ /// The column alias
+ /// The Sql statement.
+ public static Sql SelectAs(this Sql sql, Expression> field, string alias)
+ {
+ return sql.Select(string.Join(", ", sql.GetColumns(columnExpressions: new[] { field }, withAlias: false)) + " AS " + sql.SqlContext.SqlSyntax.GetQuotedColumnName(alias));
+ }
+
+ ///
+ /// Adds columns to a SELECT Sql statement.
+ ///
+ /// The type of the DTO to select.
+ /// The origin sql.
+ /// Expressions indicating the columns to select.
+ /// The Sql statement.
+ ///
+ /// If is empty, all columns are selected.
+ ///
+ public static Sql AndSelect(this Sql sql, params Expression>[] fields)
+ {
+ return sql.Append(", " + string.Join(", ", sql.GetColumns(columnExpressions: fields)));
+ }
+
+ ///
+ /// Adds an aliased column to a SELECT Sql statement.
+ ///
+ /// The type of the DTO to select.
+ /// The origin sql.
+ /// Expression indicating the column to select.
+ /// The column alias
+ /// The Sql statement.
+ public static Sql AndSelectAs(this Sql sql, Expression> field, string alias)
+ {
+ return sql.Append(", " + string.Join(", ", sql.GetColumns(columnExpressions: new[] { field }, withAlias: false)) + " AS " + sql.SqlContext.SqlSyntax.GetQuotedColumnName(alias));
+ }
+
///
/// Creates a SELECT Sql statement with a referenced Dto.
///
diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs
index fd4e5f609f..4d33977c72 100644
--- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs
+++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence.Querying
if (declaring != typeof (SqlTemplate))
return base.VisitMethodCall(m);
- if (m.Method.Name != "ArgValue" && m.Method.Name != "ArgValueIn")
+ if (m.Method.Name != "Arg" && m.Method.Name != "ArgIn")
throw new NotSupportedException($"Method SqlTemplate.{m.Method.Name} is not supported.");
var parameters = m.Method.GetParameters();
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 32dc955914..a70db29ebf 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -210,7 +210,8 @@ namespace Umbraco.Core.Persistence.Repositories
protected override void PersistNewItem(IContent entity)
{
- ((Content) entity).AddingEntity();
+ var content = (Content) entity;
+ content.AddingEntity();
// ensure that the default template is assigned
if (entity.Template == null)
@@ -224,7 +225,7 @@ namespace Umbraco.Core.Persistence.Repositories
entity.SanitizeEntityPropertiesForXmlStorage();
// create the dto
- var dto = ContentFactory.BuildDto(entity);
+ var dto = ContentFactory.BuildDto(entity, NodeObjectTypeId);
// derive path and level from parent
var parent = GetParentNodeDto(entity.ParentId);
@@ -274,27 +275,61 @@ namespace Umbraco.Core.Persistence.Repositories
documentVersionDto.Id = contentVersionDto.Id;
Database.Insert(documentVersionDto);
- // persist the document dto
- dto.NodeId = nodeDto.NodeId;
- Database.Insert(dto);
-
// persist the property data
- var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties);
+ var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties, out var edited);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
+ // persist the document dto
+ // at that point, when publishing, the entity still has its old Published value
+ // so we need to explicitely update the dto to persist the correct value
+ if (content.PublishedState == PublishedState.Publishing)
+ {
+ dto.Published = true;
+ dto.PublishName = dto.DocumentVersionDto.ContentVersionDto.Text;
+ dto.PublishTemplateId = dto.DocumentVersionDto.TemplateId;
+ dto.PublishUserId = dto.ContentDto.WriterUserId;
+ dto.PublishDate = dto.DocumentVersionDto.ContentVersionDto.VersionDate;
+ } // else it all stays null
+ dto.NodeId = nodeDto.NodeId;
+ dto.Edited = edited;
+ Database.Insert(dto);
+
+ OnUowRefreshedEntity(new UnitOfWorkEntityEventArgs(UnitOfWork, entity));
+
+ // flip the entity's published property
+ // this also flips its published state
+ if (content.PublishedState == PublishedState.Publishing)
+ {
+ content.Published = true;
+ content.PublishName = dto.PublishName;
+ content.PublishTemplate = content.Template;
+ content.PublisherId = dto.PublishUserId;
+ content.PublishDate = dto.ContentDto.UpdateDate;
+ }
+ else if (content.PublishedState == PublishedState.Published)
+ {
+ // saving a 'published' one... that one is special for rollback
+ content.PublishName = dto.DocumentVersionDto.ContentVersionDto.Text;
+ content.PublishTemplate = content.Template;
+ content.PublisherId = dto.ContentDto.WriterUserId;
+ content.PublishDate = dto.DocumentVersionDto.ContentVersionDto.VersionDate;
+ }
+ else if (content.PublishedState == PublishedState.Unpublishing)
+ {
+ content.Published = false;
+ content.PublishName = null;
+ content.PublishTemplate = null;
+ content.PublisherId = null;
+ content.PublishDate = null;
+ }
+
+ ((Content) entity).Edited = dto.Edited;
+
// if published, set tags accordingly
if (entity.Published)
UpdateEntityTags(entity, _tagRepository);
- // published => update published version infos, else leave it blank
- if (entity.Published)
- {
- ((Content) entity).PublishedDate = contentDto.UpdateDate;
- }
-
- OnUowRefreshedEntity(new UnitOfWorkEntityEventArgs(UnitOfWork, entity));
-
entity.ResetDirtyProperties();
}
@@ -309,19 +344,33 @@ namespace Umbraco.Core.Persistence.Repositories
return; // no change to save, do nothing, don't even update dates
}
+ // whatever we do, we must check that we are saving the current version
+ var version = Database.Fetch(SqlContext.Sql().Select().From().Where(x => x.VersionId == entity.Version)).FirstOrDefault();
+ if (version == null || !version.Current )
+ throw new InvalidOperationException("Cannot save a non-current version.");
+
// check if we need to create a new version
var requiresNewVersion = content.PublishedState == PublishedState.Publishing && content.Published || content.PublishedState == PublishedState.Unpublishing;
if (requiresNewVersion)
{
// drop all draft infos for the current version, won't need it anymore
- var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == entity.Version && x.Published);
+ var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == entity.Version && !x.Published);
Database.Execute(deletePropertyDataSql);
// current version is not current anymore
- var updateCurrentSql = SqlContext.Sql()
- .Update(u => u.Set(x => x.Current, false))
+ var updateContentVersionSql = (entity.Published
+ ? SqlContext.Sql().Update(u => u.Set(x => x.Current, false).Set(x => x.Text, entity.PublishName).Set(x => x.VersionDate, entity.PublishDate))
+ : SqlContext.Sql().Update(u => u.Set(x => x.Current, false)))
.Where(x => x.VersionId == content.Version);
- Database.Execute(updateCurrentSql);
+ Database.Execute(updateContentVersionSql);
+
+ if (entity.Published)
+ {
+ var updateDocumentVersionSql = SqlContext.Sql()
+ .Update(u => u.Set(x => x.TemplateId, entity.PublishTemplate?.Id))
+ .Where(x => x.Id == version.Id);
+ Database.Execute(updateDocumentVersionSql);
+ }
// resets identifiers ie get a new version id
content.UpdatingEntity();
@@ -351,7 +400,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
// create the dto
- var dto = ContentFactory.BuildDto(entity);
+ var dto = ContentFactory.BuildDto(entity, NodeObjectTypeId);
// update the node dto
var nodeDto = dto.ContentDto.NodeDto;
@@ -374,70 +423,65 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
- // fixme this pk thing is annoying - could we store that ID somewhere?
- var id = Database.ExecuteScalar(SqlContext.Sql().Select(x => x.Id).From().Where(x => x.VersionId == entity.Version));
- contentVersionDto.Id = id;
+ contentVersionDto.Id = version.Id;
Database.Update(contentVersionDto);
- documentVersionDto.Id = id;
+ documentVersionDto.Id = version.Id;
Database.Update(documentVersionDto);
}
- // update the document dto
- // at that point, when un/publishing, the entity still has its old Published value
- // so we need to explicitely update the dto to persist the correct value
- if (content.PublishedState == PublishedState.Publishing)
- dto.Published = true;
- else if (content.PublishedState == PublishedState.Unpublishing)
- dto.Published = false;
- Database.Update(dto);
-
// replace the property data
if (!requiresNewVersion)
{
var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == entity.Version);
Database.Execute(deletePropertyDataSql);
}
- var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties);
+ var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties, out var edited);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
- // update tags
- if (HasTagProperty(entity)) // fixme - what-if it had and now has not?
+ // update the document dto
+ // at that point, when un/publishing, the entity still has its old Published value
+ // so we need to explicitely update the dto to persist the correct value
+ if (content.PublishedState == PublishedState.Publishing)
{
- switch (content.PublishedState)
- {
- case PublishedState.Publishing:
- // explicitely publishing, must update tags
- UpdateEntityTags(entity, _tagRepository);
- break;
- case PublishedState.Unpublishing:
- // explicitely unpublishing, must clear tags
- ClearEntityTags(entity, _tagRepository);
- break;
- case PublishedState.Published:
- case PublishedState.Unpublished:
- // no change, depends on path-published
- // that should take care of trashing and un-trashing
- // fixme why, how would that work at all???
- if (IsPathPublished(entity)) // slightly expensive ;-(
- UpdateEntityTags(entity, _tagRepository);
- else
- ClearEntityTags(entity, _tagRepository);
- break;
- }
+ dto.Published = true;
+ dto.PublishName = dto.DocumentVersionDto.ContentVersionDto.Text;
+ dto.PublishTemplateId = dto.DocumentVersionDto.TemplateId;
+ dto.PublishUserId = dto.ContentDto.WriterUserId;
+ dto.PublishDate = dto.DocumentVersionDto.ContentVersionDto.VersionDate;
}
+ else if (content.PublishedState == PublishedState.Unpublishing)
+ dto.Published = false; // and everything is null 'cos not mapped in factories
+ dto.Edited = edited;
+ Database.Update(dto);
+
+ // if entity is publishing, update tags, else leave tags there
+ // means that implicitely unpublished, or trashed, entities *still* have tags in db
+ if (content.PublishedState == PublishedState.Publishing)
+ UpdateEntityTags(entity, _tagRepository);
+
+ OnUowRefreshedEntity(new UnitOfWorkEntityEventArgs(UnitOfWork, entity));
// flip the entity's published property
// this also flips its published state
if (content.PublishedState == PublishedState.Publishing)
+ {
content.Published = true;
+ content.PublishName = dto.PublishName;
+ content.PublishTemplate = content.Template;
+ content.PublisherId = dto.PublishUserId;
+ content.PublishDate = dto.ContentDto.UpdateDate;
+ }
else if (content.PublishedState == PublishedState.Unpublishing)
+ {
content.Published = false;
+ content.PublishName = null;
+ content.PublishTemplate = null;
+ content.PublisherId = null;
+ content.PublishDate = null;
+ }
- if (content.Published)
- content.PublishedDate = dto.ContentDto.UpdateDate;
-
- OnUowRefreshedEntity(new UnitOfWorkEntityEventArgs(UnitOfWork, entity));
+ ((Content) entity).Edited = dto.Edited;
entity.ResetDirtyProperties();
}
@@ -556,7 +600,7 @@ namespace Umbraco.Core.Persistence.Repositories
// succeed fast
if (content.ParentId == -1)
- return content.HasPublishedVersion;
+ return content.Published;
var ids = content.Path.Split(',').Skip(1).Select(int.Parse);
@@ -721,15 +765,19 @@ namespace Umbraco.Core.Persistence.Repositories
var c = content[i] = ContentFactory.BuildEntity(dto, contentType);
- // need template
+ // need templates
var templateId = dto.DocumentVersionDto.TemplateId;
+ if (templateId.HasValue && templateId.Value > 0)
+ templateIds.Add(templateId.Value);
+ templateId = dto.PublishTemplateId;
if (templateId.HasValue && templateId.Value > 0)
templateIds.Add(templateId.Value);
// need properties
temps.Add(new TempContent(dto.NodeId, versionId, contentType, c)
{
- TemplateId = dto.DocumentVersionDto.TemplateId
+ Template1Id = dto.DocumentVersionDto.TemplateId,
+ Template2Id = dto.PublishTemplateId
});
}
@@ -744,7 +792,9 @@ namespace Umbraco.Core.Persistence.Repositories
foreach (var temp in temps)
{
// complete the item
- if (temp.TemplateId.HasValue && templates.TryGetValue(temp.TemplateId.Value, out var template))
+ if (temp.Template1Id.HasValue && templates.TryGetValue(temp.Template1Id.Value, out var template))
+ temp.Content.Template = template;
+ if (temp.Template2Id.HasValue && templates.TryGetValue(temp.Template2Id.Value, out template))
temp.Content.Template = template;
temp.Content.Properties = properties[temp.VersionId];
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs
index a9d2867dc7..c4c65962ab 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs
@@ -28,6 +28,8 @@ namespace Umbraco.Core.Persistence.Repositories
_templateRepository = templateRepository;
}
+ protected override bool IsPublishing => ContentType.IsPublishingConst;
+
protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache)
{
return new FullDataSetRepositoryCachePolicy(runtimeCache, GetEntityId, /*expires:*/ true);
@@ -61,11 +63,11 @@ namespace Umbraco.Core.Persistence.Repositories
if (ids.Any())
{
//NOTE: This logic should never be executed according to our cache policy
- return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, this, _templateRepository)
+ return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, IsPublishing, this, _templateRepository)
.Where(x => ids.Contains(x.Id));
}
- return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, this, _templateRepository);
+ return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, IsPublishing, this, _templateRepository);
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs
index c7ed5b17cc..37530d49e7 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs
@@ -34,6 +34,8 @@ namespace Umbraco.Core.Persistence.Repositories
: base(work, cache, logger)
{ }
+ protected abstract bool IsPublishing { get; }
+
public IEnumerable> Move(TEntity moving, EntityContainer container)
{
var parentId = Constants.System.Root;
@@ -480,7 +482,7 @@ AND umbracoNode.id <> @id",
.Fetch(sql);
var propertyGroupFactory = new PropertyGroupFactory(id, createDate, updateDate, CreatePropertyType);
- var propertyGroups = propertyGroupFactory.BuildEntity(dtos);
+ var propertyGroups = propertyGroupFactory.BuildEntity(dtos, IsPublishing);
return new PropertyGroupCollection(propertyGroups);
}
@@ -497,7 +499,7 @@ AND umbracoNode.id <> @id",
//TODO Move this to a PropertyTypeFactory
var list = new List();
- foreach (var dto in dtos.Where(x => (x.PropertyTypeGroupId > 0) == false))
+ foreach (var dto in dtos.Where(x => x.PropertyTypeGroupId <= 0))
{
var propType = CreatePropertyType(dto.DataTypeDto.PropertyEditorAlias, dto.DataTypeDto.DbType.EnumParse(true), dto.Alias);
propType.DataTypeDefinitionId = dto.DataTypeId;
@@ -515,7 +517,7 @@ AND umbracoNode.id <> @id",
//Reset dirty properties
Parallel.ForEach(list, currentFile => currentFile.ResetDirtyProperties(false));
- return new PropertyTypeCollection(list);
+ return new PropertyTypeCollection(IsPublishing, list);
}
protected void ValidateAlias(PropertyType pt)
@@ -584,7 +586,6 @@ AND umbracoNode.id <> @id",
internal static class ContentTypeQueryMapper
{
-
public class AssociatedTemplate
{
public AssociatedTemplate(int templateId, string @alias, string templateName)
@@ -618,7 +619,7 @@ AND umbracoNode.id <> @id",
}
public static IEnumerable GetMediaTypes(
- IDatabase db, ISqlSyntaxProvider sqlSyntax,
+ IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
TRepo contentTypeRepository)
where TRepo : IReadRepository
{
@@ -626,13 +627,13 @@ AND umbracoNode.id <> @id",
var mediaTypes = MapMediaTypes(db, sqlSyntax, out allParentMediaTypeIds)
.ToArray();
- MapContentTypeChildren(mediaTypes, db, sqlSyntax, contentTypeRepository, allParentMediaTypeIds);
+ MapContentTypeChildren(mediaTypes, db, sqlSyntax, isPublishing, contentTypeRepository, allParentMediaTypeIds);
return mediaTypes;
}
public static IEnumerable GetContentTypes(
- IDatabase db, ISqlSyntaxProvider sqlSyntax,
+ IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
TRepo contentTypeRepository,
ITemplateRepository templateRepository)
where TRepo : IReadRepository
@@ -647,15 +648,14 @@ AND umbracoNode.id <> @id",
MapContentTypeTemplates(
contentTypes, db, contentTypeRepository, templateRepository, allAssociatedTemplates);
- MapContentTypeChildren(
- contentTypes, db, sqlSyntax, contentTypeRepository, allParentContentTypeIds);
+ MapContentTypeChildren(contentTypes, db, sqlSyntax, isPublishing, contentTypeRepository, allParentContentTypeIds);
}
return contentTypes;
}
internal static void MapContentTypeChildren(IContentTypeComposition[] contentTypes,
- IDatabase db, ISqlSyntaxProvider sqlSyntax,
+ IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
TRepo contentTypeRepository,
IDictionary> allParentContentTypeIds)
where TRepo : IReadRepository
@@ -665,7 +665,7 @@ AND umbracoNode.id <> @id",
var ids = contentTypes.Select(x => x.Id).ToArray();
IDictionary allPropGroups;
IDictionary allPropTypes;
- MapGroupsAndProperties(ids, db, sqlSyntax, out allPropTypes, out allPropGroups);
+ MapGroupsAndProperties(ids, db, sqlSyntax, isPublishing, out allPropTypes, out allPropGroups);
foreach (var contentType in contentTypes)
{
@@ -1068,7 +1068,7 @@ AND umbracoNode.id <> @id",
return contentType;
}
- internal static void MapGroupsAndProperties(int[] contentTypeIds, IDatabase db, ISqlSyntaxProvider sqlSyntax,
+ internal static void MapGroupsAndProperties(int[] contentTypeIds, IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
out IDictionary allPropertyTypeCollection,
out IDictionary allPropertyGroupCollection)
{
@@ -1114,7 +1114,7 @@ ORDER BY contentTypeId, groupId, id";
foreach (var contentTypeId in contentTypeIds)
{
- var propertyTypeCollection = allPropertyTypeCollection[contentTypeId] = new PropertyTypeCollection();
+ var propertyTypeCollection = allPropertyTypeCollection[contentTypeId] = new PropertyTypeCollection(isPublishing);
var propertyGroupCollection = allPropertyGroupCollection[contentTypeId] = new PropertyGroupCollection();
while (prop != null && prop.contentTypeId == contentTypeId && prop.groupId == null)
@@ -1125,7 +1125,7 @@ ORDER BY contentTypeId, groupId, id";
while (group != null && group.contentTypeId == contentTypeId)
{
- var propertyGroup = new PropertyGroup(new PropertyTypeCollection())
+ var propertyGroup = new PropertyGroup(new PropertyTypeCollection(isPublishing))
{
Id = group.id,
Name = group.text,
diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs
index 37aebf5086..2b4c38bcd3 100644
--- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs
@@ -457,9 +457,9 @@ AND umbracoNode.id <> @id",
private string EnsureUniqueNodeName(string nodeName, int id = 0)
{
var template = SqlContext.Templates.Get("Umbraco.Core.DataTypeDefinitionRepository.EnsureUniqueNodeName", tsql => tsql
- .Select(x => x.NodeId, x => x.Text)
+ .SelectAs(x => x.NodeId, "id").AndSelectAs(x => x.Text, "name")
.From()
- .Where(x => x.NodeObjectType == SqlTemplate.ArgValue("nodeObjectType")));
+ .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType")));
var sql = template.Sql(NodeObjectTypeId);
var names = Database.Fetch(sql);
diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs
index 5a69b38f20..731b1fc6c1 100644
--- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs
@@ -40,7 +40,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IDictionaryItem PerformGet(int id)
{
var sql = GetBaseQuery(false)
- .Where(GetBaseWhereClause(), new { Id = id })
+ .Where(GetBaseWhereClause(), new { id = id })
.OrderBy(x => x.UniqueId);
var dto = Database
@@ -328,7 +328,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override object GetBaseWhereClauseArguments(Guid id)
{
- return new { Id = id };
+ return new { id = id };
}
protected override string GetWhereInClauseForGetAll()
@@ -382,7 +382,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override object GetBaseWhereClauseArguments(string id)
{
- return new { Id = id };
+ return new { id = id };
}
protected override string GetWhereInClauseForGetAll()
diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs
index b8292f6e9d..1c257f9f07 100644
--- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs
@@ -19,18 +19,14 @@ namespace Umbraco.Core.Persistence.Repositories
///
/// This is limited to objects that are based in the umbracoNode-table.
///
- internal class EntityRepository : DisposableObject, IEntityRepository
+ internal class EntityRepository : IEntityRepository
{
public EntityRepository(IScopeUnitOfWork work)
{
UnitOfWork = work;
}
- ///
- /// Gets the repository's unit of work.
- ///
protected internal IScopeUnitOfWork UnitOfWork { get; }
-
protected Sql Sql() => UnitOfWork.SqlContext.Sql();
#region Query Methods
@@ -71,8 +67,8 @@ namespace Umbraco.Core.Persistence.Repositories
foreach (var idGroup in ids)
{
var propSql = GetPropertySql(Constants.ObjectTypes.Media)
- .Where("nodeId IN (@ids)", new { ids = idGroup })
- .OrderBy("nodeId");
+ .WhereIn(x => x.NodeId, idGroup)
+ .OrderBy(x => x.NodeId);
//This does NOT fetch all data into memory in a list, this will read
// over the records as a data reader, this is much better for performance and memory,
@@ -185,7 +181,7 @@ namespace Umbraco.Core.Persistence.Repositories
// else
- //query = read forward data reader, do not load everything into mem
+ // query = read forward data reader, do not load everything into mem
// fixme wtf is this collection thing?!
var dtos = UnitOfWork.Database.Query(sql);
var collection = new EntityDefinitionCollection();
@@ -245,8 +241,9 @@ namespace Umbraco.Core.Persistence.Repositories
{
collection.AddOrUpdate(new EntityDefinition(factory, dto, isContent, false));
}
+
var found = collection.FirstOrDefault();
- return found != null ? found.BuildFromDynamic() : null;
+ return found?.BuildFromDynamic();
}
public virtual IEnumerable GetAll(Guid objectTypeId, params int[] ids)
@@ -259,7 +256,7 @@ namespace Umbraco.Core.Persistence.Repositories
public virtual IEnumerable GetAll(Guid objectTypeId, params Guid[] keys)
{
return keys.Any()
- ? PerformGetAll(objectTypeId, sql => sql.Where(" umbracoNode.uniqueID in (@keys)", new { keys }))
+ ? PerformGetAll(objectTypeId, sql => sql.Where(" umbracoNode.uniqueId in (@keys)", new { keys }))
: PerformGetAll(objectTypeId);
}
@@ -385,8 +382,7 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
-
- #region Sql Statements
+ #region Sql
protected Sql GetFullSqlForEntityType(Guid key, bool isContent, bool isMedia, Guid objectTypeId)
{
@@ -418,15 +414,14 @@ namespace Umbraco.Core.Persistence.Repositories
private Sql GetPropertySql(Guid nodeObjectType)
{
var sql = Sql()
- .Select("nodeId, versionId, varcharValue, textValue, propertyEditorAlias, alias as propertyTypeAlias")
+ .Select(x => x.NodeId, x => x.VersionId, x => x.TextValue, x => x.VarcharValue)
+ .AndSelect(x => x.PropertyEditorAlias)
+ .AndSelectAs(x => x.Alias, "propertyTypeAlias")
.From()
- .InnerJoin()
- .On(dto => dto.NodeId, dto => dto.NodeId)
- .InnerJoin()
- .On(dto => dto.Id, dto => dto.PropertyTypeId)
- .InnerJoin()
- .On(dto => dto.DataTypeId, dto => dto.DataTypeId)
- .Where("umbracoNode.nodeObjectType = @nodeObjectType", new { nodeObjectType });
+ .InnerJoin().On(dto => dto.NodeId, dto => dto.NodeId)
+ .InnerJoin().On(dto => dto.PropertyTypeId, dto => dto.Id)
+ .InnerJoin().On(dto => dto.DataTypeId, dto => dto.DataTypeId)
+ .Where(x => x.NodeObjectType == nodeObjectType);
return sql;
}
@@ -439,7 +434,7 @@ namespace Umbraco.Core.Persistence.Repositories
filter?.Invoke(sql);
- //We're going to create a query to query against the entity SQL
+ // We're going to create a query to query against the entity SQL
// because we cannot group by nText columns and we have a COUNT in the entitySql we cannot simply left join
// the entitySql query, we have to join the wrapped query to get the ntext in the result
@@ -482,22 +477,22 @@ namespace Umbraco.Core.Persistence.Repositories
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate",
- "COUNT(parent.parentID) as children"
+ "COUNT(child.id) as children"
});
if (isContent)
{
- columns.Add("published.versionId as publishedVersion");
- columns.Add("document.versionId as newestVersion");
- columns.Add("contentversion.id as versionId");
+ columns.Add("contentVersion.versionId as versionId");
+ columns.Add("document.published");
+ columns.Add("document.edited");
}
if (isContent || isMedia)
{
- columns.Add("contenttype.alias");
- columns.Add("contenttype.icon");
- columns.Add("contenttype.thumbnail");
- columns.Add("contenttype.isContainer");
+ columns.Add("contentType.alias");
+ columns.Add("contentType.icon");
+ columns.Add("contentType.thumbnail");
+ columns.Add("contentType.isContainer");
}
}
@@ -505,31 +500,25 @@ namespace Umbraco.Core.Persistence.Repositories
var entitySql = Sql()
.Select(columns.ToArray())
- .From("umbracoNode umbracoNode");
+ .From();
if (isContent || isMedia)
{
entitySql
- .InnerJoin("uContent content").On("content.nodeId = umbracoNode.id");
+ .InnerJoin().On((left, right) => left.NodeId == right.NodeId)
+ .LeftJoin("contentType").On((left, right) => left.ContentTypeId == right.NodeId, aliasRight: "contentType");
}
if (isContent)
{
entitySql
- .InnerJoin("cmsDocument document").On("document.nodeId = umbracoNode.id")
- .InnerJoin("cmsContentVersion contentversion").On("contentversion.VersionId = document.versionId")
- .LeftJoin("(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1 GROUP BY nodeId, versionId) as published").On("umbracoNode.id = published.nodeId");
- //.LeftJoin("(SELECT nodeId, versionId FROM cmsDocument WHERE newest = 1 GROUP BY nodeId, versionId) as latest").On("umbracoNode.id = latest.nodeId");
- }
-
- if (isContent || isMedia)
- {
- entitySql
- .LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType");
+ .InnerJoin("contentVersion").On((left, right) => left.NodeId == right.NodeId && right.Current, aliasRight: "contentVersion")
+ .InnerJoin("document").On((left, right) => left.NodeId == right.NodeId, aliasRight: "document");
}
if (isCount == false)
- entitySql.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
+ entitySql
+ .LeftJoin("child").On((left, right) => left.NodeId == right.ParentId, aliasRight: "child");
customFilter?.Invoke(entitySql);
@@ -539,18 +528,14 @@ namespace Umbraco.Core.Persistence.Repositories
protected virtual Sql GetBaseWhere(Func>, Sql> baseQuery, bool isContent, bool isMedia, Action> filter, Guid nodeObjectType)
{
var sql = baseQuery(isContent, isMedia, filter)
- .Where("umbracoNode.nodeObjectType = @nodeObjectType", new { nodeObjectType });
- if (isContent)
- sql.Where("document.newest = 1");
+ .Where(x => x.NodeObjectType == nodeObjectType);
return sql;
}
protected virtual Sql GetBaseWhere(Func