More content refactoring (tests)

This commit is contained in:
Stephan
2017-11-15 08:53:20 +01:00
parent fcf2b27953
commit ef11fda272
108 changed files with 1803 additions and 1804 deletions
+8 -10
View File
@@ -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;
}
}
+18 -19
View File
@@ -157,6 +157,9 @@ namespace Umbraco.Core.Models
}
}
[IgnoreDataMember]
public bool Edited { get; internal set; }
/// <summary>
/// Language of the data contained within this Content object.
/// </summary>
@@ -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; }
/// <summary>
/// Changes the <see cref="ContentType"/> for the current content object
/// </summary>
@@ -242,23 +260,6 @@ namespace Umbraco.Core.Models
ChangeContentType(contentType);
}
/// <summary>
/// Gets or sets the unique identifier of the published version, if any.
/// </summary>
[IgnoreDataMember]
public Guid PublishedVersionGuid { get; internal set; }
/// <summary>
/// Gets a value indicating whether the content has a published version.
/// </summary>
public bool HasPublishedVersion => 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;
}
+103
View File
@@ -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);
}
}
}
/// <summary>
/// Sets the neutral (draft) value of a property.
/// </summary>
+65 -79
View File
@@ -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);
}
/// <summary>
/// Gets the <see cref="IProfile"/> for the Writer of this content.
/// </summary>
public static IProfile GetWriterProfile(this IMedia content, IUserService userService)
{
return userService.GetProfileById(content.WriterId);
}
#endregion
/// <summary>
/// Checks whether an <see cref="IContent"/> item has any published versions
/// </summary>
/// <param name="content"></param>
/// <returns>True if the content has any published versiom otherwise False</returns>
[Obsolete("Use the HasPublishedVersion property.", false)]
public static bool HasPublishedVersion(this IContent content)
{
return content.HasPublishedVersion;
}
#region Tag methods
#region Tags
/// <summary>
/// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags.
/// Sets tags.
/// </summary>
/// <param name="content">The content item to assign the tags to</param>
/// <param name="propertyTypeAlias">The property alias to assign the tags to</param>
/// <param name="tags">The tags to assign</param>
/// <param name="replaceTags">True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones</param>
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
/// <returns></returns>
public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
{
content.SetTags(TagCacheStorageType.Csv, propertyTypeAlias, tags, replaceTags, tagGroup);
}
/// <summary>
/// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags.
/// </summary>
/// <param name="content">The content item to assign the tags to</param>
/// <param name="storageType">The tag storage type in cache (default is csv)</param>
/// <param name="propertyTypeAlias">The property alias to assign the tags to</param>
/// <param name="tags">The tags to assign</param>
/// <param name="replaceTags">True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones</param>
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
/// <returns></returns>
public static void SetTags(this IContentBase content, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
/// <param name="content">The content item.</param>
/// <param name="propertyTypeAlias">The property alias.</param>
/// <param name="tags">The tags.</param>
/// <param name="replaceTags">True to replace the tags with the specified tags or false to merge them with the currently assigned ones.</param>
/// <param name="tagGroup">The tags group.</param>
/// <param name="storage">The tags storage type.</param>
public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> 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<string> tags, bool replaceTags, string tagGroup = "default")
internal static void SetTags(this Property property, string propertyTypeAlias, IEnumerable<string> 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<string, string>(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<string, string>(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<string> 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<JArray>(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<JArray>(property.GetValue().ToString()).Select(x => x.ToString());
property.SetValue(JsonConvert.SerializeObject(currentTags.Union(trimmedTags).ToArray())); // json array
break;
}
}
}
/// <summary>
/// Remove any of the tags specified in the collection from the property if they are currently assigned.
/// Remove tags.
/// </summary>
/// <param name="content"></param>
/// <param name="propertyTypeAlias"></param>
/// <param name="tags"></param>
/// <param name="tagGroup">The group/category that the tags are currently assigned to, the default value is "default"</param>
/// <param name="content">The content item.</param>
/// <param name="propertyTypeAlias">The property alias.</param>
/// <param name="tags">The tags.</param>
/// <param name="tagGroup">The tags group.</param>
// fixme - totally not ok with variants
public static void RemoveTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, string tagGroup = "default")
{
var property = content.Properties[propertyTypeAlias];
if (property == null)
{
throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias);
}
var trimmedTags = tags.Select(x => x.Trim()).ToArray();
var changes = property.TagChanges;
property.TagSupport.Behavior = PropertyTagBehavior.Remove;
property.TagSupport.Enable = true;
property.TagSupport.Tags = trimmedTags.Select(x => new Tuple<string, string>(x, tagGroup));
changes.Add(new PropertyTagChange
{
Type = PropertyTagChange.ChangeType.Remove,
Tags = trimmedTags.Select(x => new Tuple<string, string>(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<string> 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<JArray>(property.GetValue().ToString()).Select(x => x.ToString());
property.SetValue(JsonConvert.SerializeObject(currentTags.Except(trimmedTags).ToArray())); // json array
break;
}
}
#endregion
+6 -1
View File
@@ -13,6 +13,9 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class ContentType : ContentTypeCompositionBase, IContentType
{
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
public const bool IsPublishingConst = true;
private int _defaultTemplate;
private IEnumerable<ITemplate> _allowedTemplates;
@@ -48,8 +51,10 @@ namespace Umbraco.Core.Models
_allowedTemplates = new List<ITemplate>();
}
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
/// <inheritdoc />
public override bool IsPublishing => IsPublishingConst;
// ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo DefaultTemplateSelector = ExpressionHelper.GetPropertyInfo<ContentType, int>(x => x.DefaultTemplateId);
+67 -63
View File
@@ -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<PropertySelectors> Ps = new Lazy<PropertySelectors>();
private Lazy<int> _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<ContentTypeSort> _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<int>(() => parentId);
_allowedContentTypes = new List<ContentTypeSort>();
_propertyGroups = new PropertyGroupCollection();
_propertyTypes = new PropertyTypeCollection();
_propertyTypes.CollectionChanged += PropertyTypesChanged;
_additionalData = new Dictionary<string, object>();
// 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<int>(() => parent.Id);
_allowedContentTypes = new List<ContentTypeSort>();
_propertyGroups = new PropertyGroupCollection();
_propertyTypes = new PropertyTypeCollection();
_propertyTypes.CollectionChanged += PropertyTypesChanged;
_additionalData = new Dictionary<string, object>();
// actually OK as IsPublishing is constant
// ReSharper disable once VirtualMemberCallInConstructor
_propertyTypes = new PropertyTypeCollection(IsPublishing);
_propertyTypes.CollectionChanged += PropertyTypesChanged;
}
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
/// <summary>
/// Gets a value indicating whether the content type is publishing.
/// </summary>
/// <remarks>
/// <para>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.</para>
/// <para>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.</para>
/// </remarks>
public abstract bool IsPublishing { get; }
// ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, string>(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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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<string, object> _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
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
IDictionary<string, object> IUmbracoEntity.AdditionalData
{
get { return _additionalData; }
}
IDictionary<string, object> IUmbracoEntity.AdditionalData => _additionalData;
/// <summary>
/// Gets or sets a list of integer Ids for allowed ContentTypes
@@ -276,12 +288,9 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual IEnumerable<ContentTypeSort> 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);
}
/// <summary>
@@ -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
/// </summary>
public IEnumerable<PropertyType> 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<int>(() => 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
/// </summary>
[IgnoreDataMember]
internal PropertyTypeCollection PropertyTypeCollection
{
get { return _propertyTypes; }
}
internal PropertyTypeCollection PropertyTypeCollection => _propertyTypes;
/// <summary>
/// Indicates whether a specific property on the current <see cref="IContent"/> entity is dirty.
@@ -14,17 +14,17 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentTypeComposition
{
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
private List<IContentTypeComposition> _contentTypeComposition = new List<IContentTypeComposition>();
internal List<int> RemovedContentTypeKeyTracker = new List<int>();
protected ContentTypeCompositionBase(int parentId) : base(parentId)
{
}
{ }
protected ContentTypeCompositionBase(IContentTypeComposition parent)
: this(parent, null)
{
}
{ }
protected ContentTypeCompositionBase(IContentTypeComposition parent, string alias)
: base(parent, alias)
@@ -32,8 +32,7 @@ namespace Umbraco.Core.Models
AddContentType(parent);
}
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
// ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo ContentTypeCompositionSelector =
@@ -46,7 +45,7 @@ namespace Umbraco.Core.Models
[DataMember]
public IEnumerable<IContentTypeComposition> 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
@@ -2,7 +2,7 @@
namespace Umbraco.Core.Models.EntityBase
{
public interface IUmbracoEntity : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
public interface IUmbracoEntity : IAggregateRoot, IRememberBeingDirty
{
/// <summary>
/// Profile of the user who created this Entity
+2 -3
View File
@@ -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
/// </remarks>
public static bool IsNewEntity(this IEntity entity)
{
var dirty = (IRememberBeingDirty)entity;
var dirty = (IRememberBeingDirty) entity;
return dirty.WasPropertyDirty("Id");
}
}
+42 -22
View File
@@ -9,40 +9,75 @@ namespace Umbraco.Core.Models
public interface IContent : IContentBase
{
/// <summary>
/// Gets or sets the template used by the Content.
/// This is used to override the default one from the ContentType.
/// Gets or sets the template used to render the content.
/// </summary>
ITemplate Template { get; set; }
/// <summary>
/// Boolean indicating whether the Content is Published or not
/// Gets a value indicating whether the content is published.
/// </summary>
bool Published { get; }
/// <summary>
/// Gets a value indicating whether the content has been edited.
/// </summary>
bool Edited { get; }
/// <summary>
/// Gets a value indicating whether the content item is a blueprint.
/// </summary>
bool Blueprint { get; }
/// <summary>
/// Gets the template used to render the published version of the content.
/// </summary>
/// <remarks>When editing the content, the template can change, but this will
/// not until the content is published.</remarks>
ITemplate PublishTemplate { get; }
/// <summary>
/// Gets the name of the published version of the content.
/// </summary>
/// <remarks>When editing the content, the name can change, but this will
/// not until the content is published.</remarks>
string PublishName { get; }
/// <summary>
/// Gets the identifier of the user who published the content.
/// </summary>
int? PublisherId { get; }
/// <summary>
/// Gets the date and time the content was published.
/// </summary>
DateTime? PublishDate { get; }
[Obsolete("This will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
string Language { get; set; }
/// <summary>
/// Gets or Sets the date the Content should be released and thus be published
/// Gets or sets the date and time the content item should be published.
/// </summary>
DateTime? ReleaseDate { get; set; }
/// <summary>
/// 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.
/// </summary>
DateTime? ExpireDate { get; set; }
/// <summary>
/// Gets the ContentType used by this content object
/// Gets the content type of this content.
/// </summary>
IContentType ContentType { get; }
/// <summary>
/// Gets the current status of the Content
/// Gets the current status of the content.
/// </summary>
ContentStatus Status { get; }
// fixme - these two should move to some kind of service
/// <summary>
/// Changes the <see cref="IContentType"/> for the current content object
/// </summary>
@@ -63,20 +98,5 @@ namespace Umbraco.Core.Models
/// </summary>
/// <returns></returns>
IContent DeepCloneWithResetIdentities();
/// <summary>
/// Gets a value indicating whether the content has a published version.
/// </summary>
bool HasPublishedVersion { get; }
/// <summary>
/// Gets the unique identifier of the published version, if any.
/// </summary>
Guid PublishedVersionGuid { get; }
/// <summary>
/// Gets a value indicating whether the content item is a blueprint.
/// </summary>
bool IsBlueprint { get; }
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ namespace Umbraco.Core.Models
IEnumerable<PropertyType> PropertyTypes { get; }
/// <summary>
/// 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.
/// </summary>
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
/// <returns>True if Property with given alias exists, otherwise False</returns>
+5
View File
@@ -10,6 +10,8 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class MediaType : ContentTypeCompositionBase, IMediaType
{
public const bool IsPublishingConst = false;
/// <summary>
/// Constuctor for creating a MediaType with the parent's id.
/// </summary>
@@ -39,6 +41,9 @@ namespace Umbraco.Core.Models
{
}
/// <inheritdoc />
public override bool IsPublishing => IsPublishingConst;
/// <summary>
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
/// </summary>
+16 -45
View File
@@ -536,66 +536,37 @@ namespace Umbraco.Core.Models
private Attempt<T> WarnIfPropertyTypeNotFoundOnGet<T>(string propertyAlias, string propertyName, T defaultVal)
{
Action doLog = () => Current.Logger.Warn<Member>(
"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<Member>($"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<T>.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<T>.Fail(defaultVal);
}
return Attempt<T>.Succeed();
}
private bool WarnIfPropertyTypeNotFoundOnSet(string propertyAlias, string propertyname)
private bool WarnIfPropertyTypeNotFoundOnSet(string propertyAlias, string propertyName)
{
Action doLog = () => Current.Logger.Warn<Member>("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<Member>($"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;
}
+5 -1
View File
@@ -12,6 +12,9 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class MemberType : ContentTypeCompositionBase, IMemberType
{
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
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<string, MemberTypePropertyProfileAccess>();
}
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
/// <inheritdoc />
public override bool IsPublishing => IsPublishingConst;
private class PropertySelectors
{
+90 -35
View File
@@ -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<PropertyTagChange> _tagChanges;
private List<PropertyValue> _values = new List<PropertyValue>();
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
}
/// <summary>
/// Returns the instance of the tag support, by default tags are not enabled
/// Gets the tag changes.
/// </summary>
internal PropertyTags TagSupport => _tagSupport;
internal List<PropertyTagChange> TagChanges => _tagChanges ?? (_tagChanges = new List<PropertyTagChange>());
/// <summary>
/// Gets a value indicating whether the property has tag changes.
/// </summary>
internal bool HasTagChanges => _tagChanges != null;
/// <summary>
/// Returns the Alias of the PropertyType, which this Property is based on
@@ -135,8 +140,7 @@ namespace Umbraco.Core.Models
/// </summary>
public object GetValue(bool published = false)
{
if (_pvalue == null) return null;
return published ? _pvalue.PublishedValue : _pvalue.DraftValue;
return _pvalue == null ? null : GetPropertyValue(_pvalue, published);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
/// Sets a (draft) neutral value.
/// Sets a (edit) neutral value.
/// </summary>
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);
}
/// <summary>
/// Sets a (draft) culture value.
/// Sets a (edit) culture value.
/// </summary>
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);
}
/// <summary>
/// Sets a (draft) segment value.
/// Sets a (edit) segment value.
/// </summary>
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
}
/// <summary>
/// Gets a value indicating whether the (draft) neutral value is valid.
/// Gets a value indicating whether the (edit) neutral value is valid.
/// </summary>
/// <remarks>An invalid value can be saved, but only valid values can be published.</remarks>
public bool IsValid()
@@ -388,7 +443,7 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// Gets a value indicating whether the (draft) culture value is valid.
/// Gets a value indicating whether the (edit) culture value is valid.
/// </summary>
/// <remarks>An invalid value can be saved, but only valid values can be published.</remarks>
public bool IsValid(int languageId)
@@ -397,7 +452,7 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// Gets a value indicating whether the (draft) segment value is valid.
/// Gets a value indicating whether the (edit) segment value is valid.
/// </summary>
/// <remarks>An invalid value can be saved, but only valid values can be published.</remarks>
public bool IsValue(int languageId, string segment)
@@ -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
{
/// <summary>
/// Creates the xml representation for the <see cref="Property"/> object
/// </summary>
/// <param name="property"><see cref="Property"/> to generate xml for</param>
/// <returns>Xml of the property and its value</returns>
public static XElement ToXml(this Property property)
{
var xmlSerializer = new EntityXmlSerializer();
return xmlSerializer.Serialize(Current.Services.DataTypeService, property);
}
}
}
+18 -27
View File
@@ -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<PropertyGroup>
{
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
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<PropertySelectors> Ps = new Lazy<PropertySelectors>();
// ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<PropertyGroup, string>(x => x.Name);
@@ -38,7 +38,7 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo<PropertyGroup, PropertyTypeCollection>(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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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<int>(() => this.Id);
}
propertyType.PropertyGroupId = new Lazy<int>(() => 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;
}
}
}
@@ -21,9 +21,7 @@ namespace Umbraco.Core.Models
internal Action OnAdd;
internal PropertyGroupCollection()
{
}
{ }
public PropertyGroupCollection(IEnumerable<PropertyGroup> groups)
{
@@ -38,7 +36,8 @@ namespace Umbraco.Core.Models
internal void Reset(IEnumerable<PropertyGroup> 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;
}
}
}
@@ -1,9 +0,0 @@
namespace Umbraco.Core.Models
{
internal enum PropertyTagBehavior
{
Replace,
Remove,
Merge
}
}
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Models
{
/// <summary>
/// A set of tag changes.
/// </summary>
internal class PropertyTagChange
{
public ChangeType Type { get; set; }
public IEnumerable<Tuple<string, string>> Tags { get; set; }
public enum ChangeType
{
Replace,
Remove,
Merge
}
}
}
-36
View File
@@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Models
{
/// <summary>
/// A property extension class that allows us to enable tags for any given property
/// </summary>
internal class PropertyTags
{
public PropertyTags()
{
Enable = false;
Behavior = PropertyTagBehavior.Merge;
}
/// <summary>
/// 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)
/// </summary>
public PropertyTagBehavior Behavior { get; set; }
/// <summary>
/// Flags the property to have tagging enabled
/// </summary>
public bool Enable { get; set; }
/// <summary>
/// The actual tags to associate - tag/group
/// </summary>
public IEnumerable<Tuple<string, string>> Tags { get; set; }
}
}
+31 -36
View File
@@ -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<PropertyType>
{
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
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)
{
}
{ }
/// <summary>
/// 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<PropertySelectors> Ps = new Lazy<PropertySelectors>();
// ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<PropertyType, string>(x => x.Name);
@@ -104,14 +102,16 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo PropertyGroupIdSelector = ExpressionHelper.GetPropertyInfo<PropertyType, Lazy<int>>(x => x.PropertyGroupId);
}
public bool IsPublishing { get; internal set; }
/// <summary>
/// Gets of Sets the Name of the PropertyType
/// </summary>
[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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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<int> PropertyGroupId
{
get { return _propertyGroupId; }
set { SetPropertyValueAndDetectChanges(value, ref _propertyGroupId, Ps.Value.PropertyGroupIdSelector); }
get => _propertyGroupId;
set => SetPropertyValueAndDetectChanges(value, ref _propertyGroupId, Ps.Value.PropertyGroupIdSelector);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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}");
}
}
@@ -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
{
/// <summary>
/// Represents a collection of <see cref="PropertyType"/> objects
/// Represents a collection of <see cref="PropertyType"/> objects.
/// </summary>
[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<PropertyType> properties)
public PropertyTypeCollection(bool isPublishing, IEnumerable<PropertyType> properties)
: this(isPublishing)
{
Reset(properties);
}
public bool IsPublishing { get; }
/// <summary>
/// Resets the collection to only contain the <see cref="PropertyType"/> instances referenced in the <paramref name="properties"/> parameter.
/// </summary>
@@ -40,12 +42,14 @@ namespace Umbraco.Core.Models
internal void Reset(IEnumerable<PropertyType> 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;
}
}
}
@@ -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; }
+51 -70
View File
@@ -13,6 +13,8 @@ namespace Umbraco.Core.Models
/// </summary>
public class UmbracoEntity : Entity, IUmbracoEntity
{
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
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<PropertySelectors> Ps = new Lazy<PropertySelectors>();
// ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, int>(x => x.CreatorId);
@@ -39,9 +39,8 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, int>(x => x.SortOrder);
public readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.Trashed);
public readonly PropertyInfo HasChildrenSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.HasChildren);
public readonly PropertyInfo IsPublishedSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.IsPublished);
public readonly PropertyInfo IsDraftSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.IsDraft);
public readonly PropertyInfo HasPendingChangesSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.HasPendingChanges);
public readonly PropertyInfo PublishedSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.Published);
public readonly PropertyInfo EditedSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.Edited);
public readonly PropertyInfo ContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, string>(x => x.ContentTypeAlias);
public readonly PropertyInfo ContentTypeIconSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, string>(x => x.ContentTypeIcon);
public readonly PropertyInfo ContentTypeThumbnailSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, string>(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<string, object> AdditionalData { get; private set; }
public IDictionary<string, object> 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);
}
@@ -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
/// <summary>
/// Buils a dto from an IContent item.
/// </summary>
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
};
@@ -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;
@@ -11,6 +11,7 @@ namespace Umbraco.Core.Persistence.Factories
public static IEnumerable<Property> BuildEntities(IReadOnlyCollection<PropertyDataDto> dtos, PropertyType[] propertyTypes)
{
var properties = new List<Property>();
var propsDtos = dtos.GroupBy(x => x.PropertyTypeId).ToDictionary(x => x.Key, x => (IEnumerable<PropertyDataDto>) 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<PropertyDataDto> BuildDtos(int nodeId, Guid versionId, IEnumerable<Property> properties)
public static IEnumerable<PropertyDataDto> BuildDtos(int nodeId, Guid versionId, IEnumerable<Property> properties, out bool edited)
{
var propertyDataDtos = new List<PropertyDataDto>();
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));
}
}
}
@@ -30,14 +30,14 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory<IEnumerable<PropertyGroup>,IEnumerable<TabDto>>
public IEnumerable<PropertyGroup> BuildEntity(IEnumerable<PropertyTypeGroupDto> groupDtos)
public IEnumerable<PropertyGroup> BuildEntity(IEnumerable<PropertyTypeGroupDto> 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;
}
@@ -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<string, object>)d;
var asDictionary = (IDictionary<string, object>) 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;
@@ -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<dynamic>(@"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<DocumentDto>(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);
}
@@ -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
/// <summary>
@@ -389,7 +396,8 @@ namespace Umbraco.Core.Persistence
{
var sql = new Sql<ISqlContext>(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);
}
/// <summary>
@@ -477,6 +485,47 @@ namespace Umbraco.Core.Persistence
return sql.Select(sql.GetColumns(columnExpressions: fields));
}
/// <summary>
/// Creates a SELECT Sql statement for an aliased column.
/// </summary>
/// <typeparam name="TDto">The type of the DTO to select.</typeparam>
/// <param name="sql">The origin sql.</param>
/// <param name="field">Expression indicating the column to select.</param>
/// <param name="alias">The column alias</param>
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> SelectAs<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, object>> field, string alias)
{
return sql.Select(string.Join(", ", sql.GetColumns(columnExpressions: new[] { field }, withAlias: false)) + " AS " + sql.SqlContext.SqlSyntax.GetQuotedColumnName(alias));
}
/// <summary>
/// Adds columns to a SELECT Sql statement.
/// </summary>
/// <typeparam name="TDto">The type of the DTO to select.</typeparam>
/// <param name="sql">The origin sql.</param>
/// <param name="fields">Expressions indicating the columns to select.</param>
/// <returns>The Sql statement.</returns>
/// <remarks>
/// <para>If <paramref name="fields"/> is empty, all columns are selected.</para>
/// </remarks>
public static Sql<ISqlContext> AndSelect<TDto>(this Sql<ISqlContext> sql, params Expression<Func<TDto, object>>[] fields)
{
return sql.Append(", " + string.Join(", ", sql.GetColumns(columnExpressions: fields)));
}
/// <summary>
/// Adds an aliased column to a SELECT Sql statement.
/// </summary>
/// <typeparam name="TDto">The type of the DTO to select.</typeparam>
/// <param name="sql">The origin sql.</param>
/// <param name="field">Expression indicating the column to select.</param>
/// <param name="alias">The column alias</param>
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> AndSelectAs<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, object>> field, string alias)
{
return sql.Append(", " + string.Join(", ", sql.GetColumns(columnExpressions: new[] { field }, withAlias: false)) + " AS " + sql.SqlContext.SqlSyntax.GetQuotedColumnName(alias));
}
/// <summary>
/// Creates a SELECT Sql statement with a referenced Dto.
/// </summary>
@@ -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();
@@ -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<ContentVersionDto>(SqlContext.Sql().Select<ContentVersionDto>().From<ContentVersionDto>().Where<ContentVersionDto>(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<PropertyDataDto>().Where<PropertyDataDto>(x => x.VersionId == entity.Version && x.Published);
var deletePropertyDataSql = SqlContext.Sql().Delete<PropertyDataDto>().Where<PropertyDataDto>(x => x.VersionId == entity.Version && !x.Published);
Database.Execute(deletePropertyDataSql);
// current version is not current anymore
var updateCurrentSql = SqlContext.Sql()
.Update<ContentVersionDto>(u => u.Set(x => x.Current, false))
var updateContentVersionSql = (entity.Published
? SqlContext.Sql().Update<ContentVersionDto>(u => u.Set(x => x.Current, false).Set(x => x.Text, entity.PublishName).Set(x => x.VersionDate, entity.PublishDate))
: SqlContext.Sql().Update<ContentVersionDto>(u => u.Set(x => x.Current, false)))
.Where<ContentVersionDto>(x => x.VersionId == content.Version);
Database.Execute(updateCurrentSql);
Database.Execute(updateContentVersionSql);
if (entity.Published)
{
var updateDocumentVersionSql = SqlContext.Sql()
.Update<DocumentVersionDto>(u => u.Set(x => x.TemplateId, entity.PublishTemplate?.Id))
.Where<DocumentVersionDto>(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<int>(SqlContext.Sql().Select<ContentVersionDto>(x => x.Id).From<ContentVersionDto>().Where<ContentVersionDto>(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<PropertyDataDto>().Where<PropertyDataDto>(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<Content>(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];
@@ -28,6 +28,8 @@ namespace Umbraco.Core.Persistence.Repositories
_templateRepository = templateRepository;
}
protected override bool IsPublishing => ContentType.IsPublishingConst;
protected override IRepositoryCachePolicy<IContentType, int> CreateCachePolicy(IRuntimeCacheProvider runtimeCache)
{
return new FullDataSetRepositoryCachePolicy<IContentType, int>(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);
}
@@ -34,6 +34,8 @@ namespace Umbraco.Core.Persistence.Repositories
: base(work, cache, logger)
{ }
protected abstract bool IsPublishing { get; }
public IEnumerable<MoveEventInfo<TEntity>> Move(TEntity moving, EntityContainer container)
{
var parentId = Constants.System.Root;
@@ -480,7 +482,7 @@ AND umbracoNode.id <> @id",
.Fetch<PropertyTypeGroupDto>(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<PropertyType>();
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<DataTypeDatabaseType>(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<IMediaType> GetMediaTypes<TRepo>(
IDatabase db, ISqlSyntaxProvider sqlSyntax,
IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
TRepo contentTypeRepository)
where TRepo : IReadRepository<int, TEntity>
{
@@ -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<IContentType> GetContentTypes<TRepo>(
IDatabase db, ISqlSyntaxProvider sqlSyntax,
IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
TRepo contentTypeRepository,
ITemplateRepository templateRepository)
where TRepo : IReadRepository<int, TEntity>
@@ -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<TRepo>(IContentTypeComposition[] contentTypes,
IDatabase db, ISqlSyntaxProvider sqlSyntax,
IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
TRepo contentTypeRepository,
IDictionary<int, List<int>> allParentContentTypeIds)
where TRepo : IReadRepository<int, TEntity>
@@ -665,7 +665,7 @@ AND umbracoNode.id <> @id",
var ids = contentTypes.Select(x => x.Id).ToArray();
IDictionary<int, PropertyGroupCollection> allPropGroups;
IDictionary<int, PropertyTypeCollection> 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<int, PropertyTypeCollection> allPropertyTypeCollection,
out IDictionary<int, PropertyGroupCollection> 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,
@@ -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<NodeDto>(x => x.NodeId, x => x.Text)
.SelectAs<NodeDto>(x => x.NodeId, "id").AndSelectAs<NodeDto>(x => x.Text, "name")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == SqlTemplate.ArgValue<Guid>("nodeObjectType")));
.Where<NodeDto>(x => x.NodeObjectType == SqlTemplate.Arg<Guid>("nodeObjectType")));
var sql = template.Sql(NodeObjectTypeId);
var names = Database.Fetch<SimilarNodeName>(sql);
@@ -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<DictionaryDto>(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()
@@ -19,18 +19,14 @@ namespace Umbraco.Core.Persistence.Repositories
/// <remarks>
/// This is limited to objects that are based in the umbracoNode-table.
/// </remarks>
internal class EntityRepository : DisposableObject, IEntityRepository
internal class EntityRepository : IEntityRepository
{
public EntityRepository(IScopeUnitOfWork work)
{
UnitOfWork = work;
}
/// <summary>
/// Gets the repository's unit of work.
/// </summary>
protected internal IScopeUnitOfWork UnitOfWork { get; }
protected Sql<ISqlContext> 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<NodeDto>(x => x.NodeId, idGroup)
.OrderBy<NodeDto>(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<dynamic>(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<IUmbracoEntity> GetAll(Guid objectTypeId, params int[] ids)
@@ -259,7 +256,7 @@ namespace Umbraco.Core.Persistence.Repositories
public virtual IEnumerable<IUmbracoEntity> 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<ISqlContext> GetFullSqlForEntityType(Guid key, bool isContent, bool isMedia, Guid objectTypeId)
{
@@ -418,15 +414,14 @@ namespace Umbraco.Core.Persistence.Repositories
private Sql<ISqlContext> GetPropertySql(Guid nodeObjectType)
{
var sql = Sql()
.Select("nodeId, versionId, varcharValue, textValue, propertyEditorAlias, alias as propertyTypeAlias")
.Select<PropertyDataDto>(x => x.NodeId, x => x.VersionId, x => x.TextValue, x => x.VarcharValue)
.AndSelect<DataTypeDto>(x => x.PropertyEditorAlias)
.AndSelectAs<PropertyTypeDto>(x => x.Alias, "propertyTypeAlias")
.From<PropertyDataDto>()
.InnerJoin<NodeDto>()
.On<PropertyDataDto, NodeDto>(dto => dto.NodeId, dto => dto.NodeId)
.InnerJoin<PropertyTypeDto>()
.On<PropertyTypeDto, PropertyDataDto>(dto => dto.Id, dto => dto.PropertyTypeId)
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(dto => dto.DataTypeId, dto => dto.DataTypeId)
.Where("umbracoNode.nodeObjectType = @nodeObjectType", new { nodeObjectType });
.InnerJoin<NodeDto>().On<PropertyDataDto, NodeDto>(dto => dto.NodeId, dto => dto.NodeId)
.InnerJoin<PropertyTypeDto>().On<PropertyDataDto, PropertyTypeDto>(dto => dto.PropertyTypeId, dto => dto.Id)
.InnerJoin<DataTypeDto>().On<PropertyTypeDto, DataTypeDto>(dto => dto.DataTypeId, dto => dto.DataTypeId)
.Where<NodeDto>(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<NodeDto>();
if (isContent || isMedia)
{
entitySql
.InnerJoin("uContent content").On("content.nodeId = umbracoNode.id");
.InnerJoin<ContentDto>().On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId)
.LeftJoin<ContentTypeDto>("contentType").On<ContentDto, ContentTypeDto>((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<ContentVersionDto>("contentVersion").On<NodeDto, ContentVersionDto>((left, right) => left.NodeId == right.NodeId && right.Current, aliasRight: "contentVersion")
.InnerJoin<DocumentDto>("document").On<NodeDto, DocumentDto>((left, right) => left.NodeId == right.NodeId, aliasRight: "document");
}
if (isCount == false)
entitySql.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
entitySql
.LeftJoin<NodeDto>("child").On<NodeDto, NodeDto>((left, right) => left.NodeId == right.ParentId, aliasRight: "child");
customFilter?.Invoke(entitySql);
@@ -539,18 +528,14 @@ namespace Umbraco.Core.Persistence.Repositories
protected virtual Sql<ISqlContext> GetBaseWhere(Func<bool, bool, Action<Sql<ISqlContext>>, Sql<ISqlContext>> baseQuery, bool isContent, bool isMedia, Action<Sql<ISqlContext>> filter, Guid nodeObjectType)
{
var sql = baseQuery(isContent, isMedia, filter)
.Where("umbracoNode.nodeObjectType = @nodeObjectType", new { nodeObjectType });
if (isContent)
sql.Where("document.newest = 1");
.Where<NodeDto>(x => x.NodeObjectType == nodeObjectType);
return sql;
}
protected virtual Sql<ISqlContext> GetBaseWhere(Func<bool, bool, Action<Sql<ISqlContext>>, Sql<ISqlContext>> baseQuery, bool isContent, bool isMedia, int id)
{
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @id", new { id });
if (isContent)
sql.Where("document.newest = 1");
.Where<NodeDto>(x => x.NodeId == id);
sql.Append(GetGroupBy(isContent, isMedia));
return sql;
}
@@ -558,9 +543,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected virtual Sql<ISqlContext> GetBaseWhere(Func<bool, bool, Action<Sql<ISqlContext>>, Sql<ISqlContext>> baseQuery, bool isContent, bool isMedia, Guid key)
{
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @uniqueID", new { uniqueID = key });
if (isContent)
sql.Where("document.newest = 1");
.Where<NodeDto>(x => x.UniqueId == key);
sql.Append(GetGroupBy(isContent, isMedia));
return sql;
}
@@ -568,18 +551,14 @@ namespace Umbraco.Core.Persistence.Repositories
protected virtual Sql<ISqlContext> GetBaseWhere(Func<bool, bool, Action<Sql<ISqlContext>>, Sql<ISqlContext>> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, int id)
{
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @id AND umbracoNode.nodeObjectType = @nodeObjectType", new { id, nodeObjectType});
if (isContent)
sql.Where("document.newest = 1");
.Where<NodeDto>(x => x.NodeId == id && x.NodeObjectType == nodeObjectType);
return sql;
}
protected virtual Sql<ISqlContext> GetBaseWhere(Func<bool, bool, Action<Sql<ISqlContext>>, Sql<ISqlContext>> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, Guid key)
{
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @uniqueID AND umbracoNode.nodeObjectType = @nodeObjectType", new { uniqueID = key, nodeObjectType });
if (isContent)
sql.Where("document.newest = 1");
.Where<NodeDto>(x => x.UniqueId == key && x.NodeObjectType == nodeObjectType);
return sql;
}
@@ -589,12 +568,12 @@ namespace Umbraco.Core.Persistence.Repositories
{
"umbracoNode.id",
"umbracoNode.trashed",
"umbracoNode.parentID",
"umbracoNode.parentId",
"umbracoNode.nodeUser",
"umbracoNode.level",
"umbracoNode.path",
"umbracoNode.sortOrder",
"umbracoNode.uniqueID",
"umbracoNode.uniqueId",
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate"
@@ -602,17 +581,17 @@ namespace Umbraco.Core.Persistence.Repositories
if (isContent)
{
columns.Add("published.versionId");
columns.Add("document.versionId");
columns.Add("contentVersion.id");
columns.Add("contentVersion.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");
}
var sql = Sql().GroupBy(columns.ToArray());
@@ -627,26 +606,15 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
/// <summary>
/// Dispose disposable properties
/// </summary>
/// <remarks>
/// Ensure the unit of work is disposed
/// </remarks>
protected override void DisposeResources()
{
UnitOfWork.DisposeIfDisposable();
}
public bool Exists(Guid key)
{
var sql = new Sql().Select("COUNT(*)").From("umbracoNode").Where("uniqueID=@uniqueID", new { uniqueID = key });
var sql = Sql().SelectCount().From<NodeDto>().Where<NodeDto>(x => x.UniqueId == key);
return UnitOfWork.Database.ExecuteScalar<int>(sql) > 0;
}
public bool Exists(int id)
{
var sql = new Sql().Select("COUNT(*)").From("umbracoNode").Where("id=@id", new { id });
var sql = Sql().SelectCount().From<NodeDto>().Where<NodeDto>(x => x.NodeId == id);
return UnitOfWork.Database.ExecuteScalar<int>(sql) > 0;
}
@@ -293,7 +293,7 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Insert(contentVersionDto);
// 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 _);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
@@ -349,7 +349,7 @@ namespace Umbraco.Core.Persistence.Repositories
// update the property data
var deletePropertyDataSql = SqlContext.Sql().Delete<PropertyDataDto>().Where<PropertyDataDto>(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 _);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
@@ -22,6 +22,8 @@ namespace Umbraco.Core.Persistence.Repositories
: base(work, cache, logger)
{ }
protected override bool IsPublishing => MediaType.IsPublishingConst;
protected override IRepositoryCachePolicy<IMediaType, int> CreateCachePolicy(IRuntimeCacheProvider runtimeCache)
{
return new FullDataSetRepositoryCachePolicy<IMediaType, int>(runtimeCache, GetEntityId, /*expires:*/ true);
@@ -55,11 +57,11 @@ namespace Umbraco.Core.Persistence.Repositories
if (ids.Any())
{
//NOTE: This logic should never be executed according to our cache policy
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, this)
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, IsPublishing, this)
.Where(x => ids.Contains(x.Id));
}
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, this);
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, IsPublishing, this);
}
protected override IEnumerable<IMediaType> PerformGetAll(params Guid[] ids)
@@ -161,8 +161,11 @@ namespace Umbraco.Core.Persistence.Repositories
.InnerJoin<MemberDto>().On<MemberDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.LeftJoin<PropertyTypeDto>().On<PropertyTypeDto, ContentDto>(left => left.ContentTypeId, right => right.ContentTypeId)
.LeftJoin<DataTypeDto>().On<DataTypeDto, PropertyTypeDto>(left => left.DataTypeId, right => right.DataTypeId)
.LeftJoin<PropertyDataDto>().On<PropertyDataDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.Append("AND " + Constants.DatabaseSchema.Tables.PropertyData + ".versionId = cmsContentVersion.VersionId")
.LeftJoin<PropertyDataDto>().On(x => x
.Where<PropertyDataDto, PropertyTypeDto>((left, right) => left.PropertyTypeId == right.Id)
.Where<PropertyDataDto, ContentVersionDto>((left, right) => left.VersionId == right.VersionId))
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
}
@@ -179,7 +182,7 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE nodeId = @id",
"DELETE FROM cmsMember2MemberGroup WHERE Member = @id",
"DELETE FROM cmsMember WHERE nodeId = @id",
"DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE ContentId = @id",
"DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id",
"DELETE FROM cmsContentXml WHERE nodeId = @id",
"DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @id",
"DELETE FROM umbracoNode WHERE id = @id"
@@ -300,7 +303,7 @@ namespace Umbraco.Core.Persistence.Repositories
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 _);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
@@ -366,7 +369,7 @@ namespace Umbraco.Core.Persistence.Repositories
// replace the property data
var deletePropertyDataSql = SqlContext.Sql().Delete<PropertyDataDto>().Where<PropertyDataDto>(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 _);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
@@ -25,6 +25,8 @@ namespace Umbraco.Core.Persistence.Repositories
: base(work, cache, logger)
{ }
protected override bool IsPublishing => MemberType.IsPublishingConst;
protected override IRepositoryCachePolicy<IMemberType, int> CreateCachePolicy(IRuntimeCacheProvider runtimeCache)
{
return new FullDataSetRepositoryCachePolicy<IMemberType, int>(runtimeCache, GetEntityId, /*expires:*/ true);
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Repositories
private int _numPos = -2;
public int Id { get; set; }
public string Text { get; set; }
public string Name { get; set; }
// cached - reused
public int NumPos
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
if (_numPos != -2) return _numPos;
var name = Text;
var name = Name;
if (name[name.Length - 1] != ')')
return _numPos = -1;
@@ -39,7 +39,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (NumPos < 0)
throw new InvalidOperationException();
int num;
if (int.TryParse(Text.Substring(NumPos + 1, Text.Length - 2 - NumPos), out num))
if (int.TryParse(Name.Substring(NumPos + 1, Name.Length - 2 - NumPos), out num))
return num;
return 0;
}
@@ -56,8 +56,8 @@ namespace Umbraco.Core.Persistence.Repositories
var xpos = x.NumPos;
var ypos = y.NumPos;
var xname = x.Text;
var yname = y.Text;
var xname = x.Name;
var yname = y.Name;
if (xpos < 0 || ypos < 0 || xpos != ypos)
return string.Compare(xname, yname, StringComparison.Ordinal);
@@ -95,12 +95,12 @@ namespace Umbraco.Core.Persistence.Repositories
if (uniqueing)
{
if (name.NumPos > 0 && name.Text.StartsWith(nodeName) && name.NumVal == uniqueNumber)
if (name.NumPos > 0 && name.Name.StartsWith(nodeName) && name.NumVal == uniqueNumber)
uniqueNumber++;
else
break;
}
else if (name.Text.InvariantEquals(nodeName))
else if (name.Name.InvariantEquals(nodeName))
{
uniqueing = true;
}
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override Task PerformGet(int id)
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
sql.Where(GetBaseWhereClause(), new { id = id });
var taskDto = Database.Fetch<TaskDto>(SqlContext.SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (taskDto == null)
@@ -169,7 +169,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IUserGroup PerformGet(int id)
{
var sql = GetBaseQuery(QueryType.Single);
sql.Where(GetBaseWhereClause(), new { Id = id });
sql.Where(GetBaseWhereClause(), new { id = id });
AppendGroupBy(sql);
sql.OrderBy<UserGroupDto>(x => x.Id); // required for references
@@ -52,7 +52,7 @@ namespace Umbraco.Core.Persistence.Repositories
var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetVersionIds", tsql =>
tsql.Select<ContentVersionDto>(x => x.VersionId)
.From<ContentVersionDto>()
.Where<ContentVersionDto>(x => x.NodeId == SqlTemplate.ArgValue<int>("nodeId"))
.Where<ContentVersionDto>(x => x.NodeId == SqlTemplate.Arg<int>("nodeId"))
.OrderByDescending<ContentVersionDto>(x => x.Current) // current '1' comes before others '0'
.AndByDescending<ContentVersionDto>(x => x.VersionDate) // most recent first
);
@@ -66,9 +66,9 @@ namespace Umbraco.Core.Persistence.Repositories
// get the version we want to delete
var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetVersion", tsql =>
tsql.Select<ContentVersionDto>().From<ContentVersionDto>().Where<ContentVersionDto>(x => x.VersionId == SqlTemplate.ArgValue<Guid>("versionId"))
tsql.Select<ContentVersionDto>().From<ContentVersionDto>().Where<ContentVersionDto>(x => x.VersionId == SqlTemplate.Arg<Guid>("versionId"))
);
var versionDto = Database.Fetch<ContentVersionDto>(template.Sql(versionId)).FirstOrDefault();
var versionDto = Database.Fetch<ContentVersionDto>(template.Sql(new { versionId })).FirstOrDefault();
// nothing to delete
if (versionDto == null)
@@ -87,10 +87,10 @@ namespace Umbraco.Core.Persistence.Repositories
// fixme test object node type?
// get the versions we want to delete, excluding the current one
var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetVersion", tsql =>
tsql.Select<ContentVersionDto>().From<ContentVersionDto>().Where<ContentVersionDto>(x => x.NodeId == SqlTemplate.ArgValue<int>("nodeId") && !x.Current && x.VersionDate < SqlTemplate.ArgValue<DateTime>("date"))
var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetVersions", tsql =>
tsql.Select<ContentVersionDto>().From<ContentVersionDto>().Where<ContentVersionDto>(x => x.NodeId == SqlTemplate.Arg<int>("nodeId") && !x.Current && x.VersionDate < SqlTemplate.Arg<DateTime>("versionDate"))
);
var versionDtos = Database.Fetch<ContentVersionDto>(template.Sql(nodeId, versionDate)); // fixme ok params?
var versionDtos = Database.Fetch<ContentVersionDto>(template.Sql(new { nodeId, versionDate }));
foreach (var versionDto in versionDtos)
PerformDeleteVersion(versionDto.NodeId, versionDto.VersionId);
}
@@ -211,38 +211,19 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
protected void UpdateEntityTags(IContentBase entity, ITagRepository tagRepo)
{
foreach (var tagProp in entity.Properties.Where(x => x.TagSupport.Enable))
foreach (var property in entity.Properties.Where(x => x.HasTagChanges))
{
if (tagProp.TagSupport.Behavior == PropertyTagBehavior.Remove)
foreach (var change in property.TagChanges)
{
//remove the specific tags
tagRepo.RemoveTagsFromProperty(
entity.Id,
tagProp.PropertyTypeId,
tagProp.TagSupport.Tags.Select(x => new Tag { Text = x.Item1, Group = x.Item2 }));
}
else
{
//assign the tags
tagRepo.AssignTagsToProperty(
entity.Id,
tagProp.PropertyTypeId,
tagProp.TagSupport.Tags.Select(x => new Tag { Text = x.Item1, Group = x.Item2 }),
tagProp.TagSupport.Behavior == PropertyTagBehavior.Replace);
var tags = change.Tags.Select(x => new Tag { Text = x.Item1, Group = x.Item2 });
if (change.Type == PropertyTagChange.ChangeType.Remove)
tagRepo.RemoveTagsFromProperty(entity.Id, property.PropertyTypeId, tags);
else
tagRepo.AssignTagsToProperty(entity.Id, property.PropertyTypeId, tags, change.Type == PropertyTagChange.ChangeType.Replace);
}
}
}
/// <summary>
/// Determines if an item has a property that supports tags.
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
protected bool HasTagProperty(IContentBase entity)
{
return entity.Properties.Any(x => x.TagSupport.Enable);
}
#endregion
// sql: the main sql
@@ -340,11 +321,16 @@ namespace Umbraco.Core.Persistence.Repositories
END AS customPropVal,
cver.nodeId AS customPropNodeId")
.From<ContentVersionDto>("cver")
.InnerJoin<PropertyDataDto>("opdata").On<ContentVersionDto, PropertyDataDto>((left, right) => left.NodeId == right.NodeId && left.VersionId == right.VersionId && left.Current, "cver", "opdata")
.InnerJoin<PropertyDataDto>("opdata").On<ContentVersionDto, PropertyDataDto>((left, right) => left.NodeId == right.NodeId && left.VersionId == right.VersionId, "cver", "opdata")
.InnerJoin<PropertyTypeDto>("optype").On<PropertyDataDto, PropertyTypeDto>((left, right) => left.PropertyTypeId == right.Id, "opdata", "optype")
.Where<ContentVersionDto>(x => x.Current, "cver")
.Where<PropertyDataDto>(x => !x.Published, "opdata") // always query on edit values
.Where<PropertyTypeDto>(x => x.Alias == "", "optype");
var innerSqlString = innerSql.SQL.Replace("@0", "1").Replace("@1", "@" + sql.Arguments.Length);
// @0 is for x.Current ie 'true' = 1
// @1 is for x.Published ie 'true' = 1
// @2 is for x.Alias
var innerSqlString = innerSql.SQL.Replace("@0", "1").Replace("@1", "1").Replace("@2", "@" + sql.Arguments.Length);
var outerJoinTempTable = $@"LEFT OUTER JOIN ({innerSqlString}) AS customPropData
ON customPropData.customPropNodeId = {Constants.DatabaseSchema.Tables.Node}.id "; // trailing space is important!
@@ -376,13 +362,13 @@ namespace Umbraco.Core.Persistence.Repositories
// start with base query, and apply the supplied IQuery
if (query == null) query = UnitOfWork.SqlContext.Query<TEntity>();
var sqlNodeIds = new SqlTranslator<TEntity>(GetBaseQuery(QueryType.Many), query).Translate();
var sql = new SqlTranslator<TEntity>(GetBaseQuery(QueryType.Many), query).Translate();
// sort and filter
sqlNodeIds = PrepareSqlForPagedResults(sqlNodeIds, filterSql, orderBy, orderDirection, orderBySystemField);
sql = PrepareSqlForPagedResults(sql, filterSql, orderBy, orderDirection, orderBySystemField);
// get a page of DTOs and the total count
var pagedResult = Database.Page<TDto>(pageIndex + 1, pageSize, sqlNodeIds);
var pagedResult = Database.Page<TDto>(pageIndex + 1, pageSize, sql);
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
// map the DTOs and return
@@ -917,9 +903,14 @@ ORDER BY nodeId, versionId, propertytypeid
public IContentTypeComposition ContentType { get; set; }
/// <summary>
/// Gets or sets the identifier of the template of the content.
/// Gets or sets the identifier of the template 1 of the content.
/// </summary>
public int? TemplateId { get; set; }
public int? Template1Id { get; set; }
/// <summary>
/// Gets or sets the identifier of the template 2 of the content.
/// </summary>
public int? Template2Id { get; set; }
}
protected class TempContent<T> : TempContent
@@ -1154,9 +1145,9 @@ ORDER BY nodeId, versionId, propertytypeid
protected virtual string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0)
{
var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.EnsureUniqueNodeName", tsql => tsql
.Select<NodeDto>(x => x.NodeId, x => x.Text)
.SelectAs<NodeDto>(x => x.NodeId, "id").AndSelectAs<NodeDto>(x => x.Text, "name")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == SqlTemplate.ArgValue<Guid>("nodeObjectType") && x.ParentId == SqlTemplate.ArgValue<int>("parentId")));
.Where<NodeDto>(x => x.NodeObjectType == SqlTemplate.Arg<Guid>("nodeObjectType") && x.ParentId == SqlTemplate.Arg<int>("parentId")));
var sql = template.Sql(NodeObjectTypeId, parentId);
var names = Database.Fetch<SimilarNodeName>(sql);
@@ -1167,7 +1158,7 @@ ORDER BY nodeId, versionId, propertytypeid
protected virtual int GetNewChildSortOrder(int parentId, int first)
{
var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetSortOrder", tsql =>
tsql.Select($"COALESCE(MAX(sortOrder),{first - 1})").From<NodeDto>().Where<NodeDto>(x => x.ParentId == SqlTemplate.ArgValue<int>("parentId") && x.NodeObjectType == NodeObjectTypeId)
tsql.Select($"COALESCE(MAX(sortOrder),{first - 1})").From<NodeDto>().Where<NodeDto>(x => x.ParentId == SqlTemplate.Arg<int>("parentId") && x.NodeObjectType == NodeObjectTypeId)
);
return Database.ExecuteScalar<int>(template.Sql(new { parentId })) + 1;
@@ -1176,7 +1167,7 @@ ORDER BY nodeId, versionId, propertytypeid
protected virtual NodeDto GetParentNodeDto(int parentId)
{
var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetParentNode", tsql =>
tsql.Select<NodeDto>().From<NodeDto>().Where<NodeDto>(x => x.NodeId == SqlTemplate.ArgValue<int>("parentId"))
tsql.Select<NodeDto>().From<NodeDto>().Where<NodeDto>(x => x.NodeId == SqlTemplate.Arg<int>("parentId"))
);
return Database.Fetch<NodeDto>(template.Sql(parentId)).First();
@@ -1185,7 +1176,7 @@ ORDER BY nodeId, versionId, propertytypeid
protected virtual int GetReservedId(Guid uniqueId)
{
var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetReservedId", tsql =>
tsql.Select<NodeDto>(x => x.NodeId).From<NodeDto>().Where<NodeDto>(x => x.UniqueId == SqlTemplate.ArgValue<Guid>("uniqueId") && x.NodeObjectType == Constants.ObjectTypes.IdReservation)
tsql.Select<NodeDto>(x => x.NodeId).From<NodeDto>().Where<NodeDto>(x => x.UniqueId == SqlTemplate.Arg<Guid>("uniqueId") && x.NodeObjectType == Constants.ObjectTypes.IdReservation)
);
var id = Database.ExecuteScalar<int?>(template.Sql(new { uniqueId = uniqueId }));
return id ?? 0;
+11 -2
View File
@@ -100,11 +100,20 @@ namespace Umbraco.Core.Persistence
new Sql<ISqlContext>(_sqlContext, _sql, _args.Values.ToArray()).WriteToConsole();
}
/// <summary>
/// Gets a named argument.
/// </summary>
public static object Arg(string name) => new TemplateArg(name);
public static T ArgValue<T>(string name) => default;
/// <summary>
/// Gets a WHERE expression argument.
/// </summary>
public static T Arg<T>(string name) => default;
public static IEnumerable<T> ArgValueIn<T>(string name)
/// <summary>
/// Gets a WHERE IN expression argument.
/// </summary>
public static IEnumerable<T> ArgIn<T>(string name)
{
// don't return an empty enumerable, as it breaks NPoco
// fixme - should we cache these arrays?
@@ -1,10 +1,13 @@
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// An interface that is shared between parameter and property value editors to access their views
/// Represents an editor for values.
/// </summary>
public interface IValueEditor
{
/// <summary>
/// Gets the editor view.
/// </summary>
string View { get; }
}
}
@@ -1,29 +1,30 @@
using Newtonsoft.Json;
namespace Umbraco.Core.PropertyEditors
namespace Umbraco.Core.PropertyEditors
{
// fixme - can we kill this and use "ValueEditor" for both macro and all?
/// <summary>
/// Represents the value editor for the parameter editor during macro parameter editing
/// Represents a value editor for macro parameters.
/// </summary>
public class ParameterValueEditor : IValueEditor
{
/// <summary>
/// default ctor
/// Initializes a new instance of the <see cref="ParameterValueEditor"/> class.
/// </summary>
public ParameterValueEditor()
{
}
{ }
/// <summary>
/// Creates a new editor with the specified view
/// Initializes a new instance of the <see cref="ParameterValueEditor"/> class.
/// </summary>
/// <param name="view"></param>
public ParameterValueEditor(string view)
: this()
{
View = view;
}
/// <summary>
/// Gets or sets the editor view.
/// </summary>
public string View { get; set; }
}
}
@@ -14,36 +14,27 @@ using Umbraco.Core.Services;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Represents the value editor for the property editor during content editing
/// Represents a value editor for content properties.
/// </summary>
/// <remarks>
/// The Json serialization attributes are required for manifest property editors to work
/// </remarks>
public class PropertyValueEditor : IValueEditor
{
/// <summary>
/// assign defaults
/// Initializes a new instance of the <see cref="PropertyValueEditor"/> class.
/// </summary>
public PropertyValueEditor()
{
ValueType = PropertyEditorValueTypes.String;
//set a default for validators
Validators = new List<IPropertyValidator>();
}
/// <summary>
/// Creates a new editor with the specified view
/// Initializes a new instance of the <see cref="PropertyValueEditor"/> class.
/// </summary>
/// <param name="view"></param>
/// <param name="validators">Allows adding custom validators during construction instead of specifying them later</param>
public PropertyValueEditor(string view, params IPropertyValidator[] validators)
: this()
{
View = view;
foreach (var v in validators)
{
Validators.Add(v);
}
Validators.AddRange(validators);
}
private PreValueCollection _preVals;
@@ -75,11 +66,12 @@ namespace Umbraco.Core.PropertyEditors
}
/// <summary>
/// Defines the view to use for the editor, this can be one of 3 things:
/// * the full virtual path or
/// * the relative path to the current Umbraco folder
/// * a simple view name which will map to the views/propertyeditors/{view}/{view}.html
/// Gets or sets the editor view.
/// </summary>
/// <remarks>
/// <para>The view can be three things: (1) the full virtual path, or (2) the relative path to the current Umbraco
/// folder, or (3) a view name which maps to views/propertyeditors/{view}/{view}.html.</para>
/// </remarks>
[JsonProperty("view", Required = Required.Always)]
public string View { get; set; }
@@ -225,6 +217,13 @@ namespace Umbraco.Core.PropertyEditors
return value.TryConvertTo(valueType);
}
// fixme - not dealing with variants here!
//
// editors should declare whether they support variants, and then we should have a common
// way of dealing with it, ie of sending and receiving values, etc.
// eg
// [ { "value": "hello" }, { "lang": "fr-fr", "value": "bonjour" } ]
/// <summary>
/// A method to deserialize the string value that has been saved in the content editor
/// to an object to be stored in the database.
@@ -258,8 +257,6 @@ namespace Umbraco.Core.PropertyEditors
return result.Result;
}
//TODO: Change the result to object so we can pass back JSON or json converted clr types if we want!
/// <summary>
/// A method used to format the database value to a value that can be used by the editor
/// </summary>
@@ -316,27 +313,55 @@ namespace Umbraco.Core.PropertyEditors
}
}
// fixme - the methods below should be replaced by proper property value convert ToXPath usage!
/// <summary>
/// Converts the property db value to an XML fragment
/// Converts a property to Xml fragments.
/// </summary>
public IEnumerable<XElement> ConvertDbToXml(Property property, IDataTypeService dataTypeService, ILocalizationService localizationService, bool published)
{
published &= property.PropertyType.IsPublishing;
var nodeName = property.PropertyType.Alias.ToSafeAlias();
foreach (var pvalue in property.Values)
{
var value = published ? pvalue.PublishedValue : pvalue.EditValue;
if (value == null || value is string stringValue && string.IsNullOrWhiteSpace(stringValue))
continue;
var xElement = new XElement(nodeName);
if (pvalue.LanguageId.HasValue)
{
var language = localizationService.GetLanguageById(pvalue.LanguageId.Value);
if (language == null) continue; // uh?
xElement.Add(new XAttribute("lang", language.IsoCode));
}
if (pvalue.Segment != null)
xElement.Add(new XAttribute("segment", pvalue.Segment));
var xValue = ConvertDbToXml(property.PropertyType, value, dataTypeService);
xElement.Add(xValue);
yield return xElement;
}
}
/// <summary>
/// Converts a property value to an Xml fragment.
/// </summary>
/// <param name="property"></param>
/// <param name="propertyType"></param>
/// <param name="dataTypeService"></param>
/// <returns></returns>
/// <remarks>
/// By default this will just return the value of ConvertDbToString but ensure that if the db value type is nvarchar or text
/// it is a CDATA fragment, otherwise it is just a text fragment.
///
/// This method by default will only return XText or XCData which must be wrapped in an element!
///
/// If the value is empty we will not return as CDATA since that will just take up more space in the file.
/// <para>By default, this returns the value of ConvertDbToString but ensures that if the db value type is
/// NVarchar or NText, the value is returned as a CDATA fragment - elxe it's a Text fragment.</para>
/// <para>Returns an XText or XCData instance which must be wrapped in a element.</para>
/// <para>If the value is empty we will not return as CDATA since that will just take up more space in the file.</para>
/// </remarks>
public virtual XNode ConvertDbToXml(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
public XNode ConvertDbToXml(PropertyType propertyType, object value, IDataTypeService dataTypeService)
{
//check for null or empty value, we don't want to return CDATA if that is the case
if (property.GetValue() == null || property.GetValue().ToString().IsNullOrWhiteSpace())
if (value == null || value.ToString().IsNullOrWhiteSpace())
{
return new XText(ConvertDbToString(property, propertyType, dataTypeService));
return new XText(ConvertDbToString(propertyType, value, dataTypeService));
}
switch (GetDatabaseType())
@@ -344,48 +369,37 @@ namespace Umbraco.Core.PropertyEditors
case DataTypeDatabaseType.Date:
case DataTypeDatabaseType.Integer:
case DataTypeDatabaseType.Decimal:
return new XText(ConvertDbToString(property, propertyType, dataTypeService));
return new XText(ConvertDbToString(propertyType, value, dataTypeService));
case DataTypeDatabaseType.Nvarchar:
case DataTypeDatabaseType.Ntext:
//put text in cdata
return new XCData(ConvertDbToString(property, propertyType, dataTypeService));
return new XCData(ConvertDbToString(propertyType, value, dataTypeService));
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Converts the property value for use in the front-end cache
/// Converts a property value to a string.
/// </summary>
/// <param name="property"></param>
/// <param name="propertyType"></param>
/// <param name="dataTypeService"></param>
/// <returns></returns>
public virtual string ConvertDbToString(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
public virtual string ConvertDbToString(PropertyType propertyType, object value, IDataTypeService dataTypeService)
{
if (property.GetValue() == null)
if (value == null)
return string.Empty;
switch (GetDatabaseType())
{
case DataTypeDatabaseType.Nvarchar:
case DataTypeDatabaseType.Ntext:
property.GetValue().ToXmlString<string>();
return property.GetValue().ToXmlString<string>();
return value.ToXmlString<string>();
case DataTypeDatabaseType.Integer:
case DataTypeDatabaseType.Decimal:
return property.GetValue().ToXmlString(property.GetValue().GetType());
return value.ToXmlString(value.GetType());
case DataTypeDatabaseType.Date:
//treat dates differently, output the format as xml format
if (property.GetValue() == null)
{
return string.Empty;
}
var date = property.GetValue().TryConvertTo<DateTime?>();
var date = value.TryConvertTo<DateTime?>();
if (date.Success == false || date.Result == null)
{
return string.Empty;
}
return date.Result.ToXmlString<DateTime>();
default:
throw new ArgumentOutOfRangeException();
+47 -122
View File
@@ -796,30 +796,6 @@ namespace Umbraco.Core.Services
return GetById(content.ParentId);
}
/// <summary>
/// Gets the published version of an <see cref="IContent"/> item
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/> to retrieve version from</param>
/// <returns>An <see cref="IContent"/> item</returns>
public IContent GetPublishedVersion(int id)
{
var version = GetVersions(id);
return version.FirstOrDefault(x => x.Published);
}
/// <summary>
/// Gets the published version of a <see cref="IContent"/> item.
/// </summary>
/// <param name="content">The content item.</param>
/// <returns>The published version, if any; otherwise, null.</returns>
public IContent GetPublishedVersion(IContent content)
{
if (content.Published) return content;
return content.HasPublishedVersion
? GetByVersion(content.PublishedVersionGuid)
: null;
}
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects, which reside at the first level / root
/// </summary>
@@ -915,23 +891,6 @@ namespace Umbraco.Core.Services
return CountChildren(id) > 0;
}
/// <summary>
/// Checks whether an <see cref="IContent"/> item has any published versions
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/></param>
/// <returns>True if the content has any published version otherwise False</returns>
public bool HasPublishedVersion(int id)
{
using (var uow = UowProvider.CreateUnitOfWork(readOnly: true))
{
uow.ReadLock(Constants.Locks.ContentTree);
var repository = uow.CreateRepository<IContentRepository>();
var query = Query<IContent>().Where(x => x.Published && x.Id == id && x.Trashed == false);
var count = repository.Count(query);
return count > 0;
}
}
/// <summary>
/// Checks if the passed in <see cref="IContent"/> can be published based on the anscestors publish state.
/// </summary>
@@ -975,7 +934,7 @@ namespace Umbraco.Core.Services
var sql = uow.SqlContext.Sql(@"
SELECT id
FROM umbracoNode
JOIN cmsDocument ON umbracoNode.id=cmsDocument.nodeId AND cmsDocument.published=@0
JOIN uDocument ON umbracoNode.id=uDocument.nodeId AND uDocument.published=@0
WHERE umbracoNode.trashed=@1 AND umbracoNode.id IN (@2)",
true, false, ids);
var x = uow.Database.Fetch<int>(sql);
@@ -1352,7 +1311,7 @@ namespace Umbraco.Core.Services
// if it's not trashed yet, and published, we should unpublish
// but... UnPublishing event makes no sense (not going to cancel?) and no need to save
// just raise the event
if (content.Trashed == false && content.HasPublishedVersion)
if (content.Trashed == false && content.Published)
uow.Events.Dispatch(UnPublished, this, new PublishEventArgs<IContent>(content, false, false), "UnPublished");
DeleteLocked(uow, repository, content);
@@ -1460,7 +1419,9 @@ namespace Umbraco.Core.Services
uow.WriteLock(Constants.Locks.ContentTree);
var repository = uow.CreateRepository<IContentRepository>();
repository.DeleteVersion(versionId);
var c = repository.Get(id);
if (c.Version != versionId) // don't delete the current version
repository.DeleteVersion(versionId);
uow.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false,/* specificVersion:*/ versionId));
Audit(uow, AuditType.Delete, "Delete Content by version performed by user", userId, Constants.System.Root);
@@ -1580,7 +1541,7 @@ namespace Umbraco.Core.Services
// if the content was trashed under another content, and so has a published version,
// it cannot move back as published but has to be unpublished first - that's for the
// root content, everything underneath will retain its published status
if (content.Trashed && content.HasPublishedVersion)
if (content.Trashed && content.Published)
{
// however, it had been masked when being trashed, so there's no need for
// any special event here - just change its state
@@ -1754,11 +1715,10 @@ namespace Umbraco.Core.Services
uow.WriteLock(Constants.Locks.ContentTree);
var repository = uow.CreateRepository<IContentRepository>();
// a copy is .Saving and will be .Unpublished
// a copy is not published (but not really unpublishing either)
// update the create author and last edit author
// fixme - not like this!
//if (copy.Published)
// copy.ChangePublishedState(PublishedState.Unpublished);
if (copy.Published)
((Content) copy).Published = false;
copy.CreatorId = userId;
copy.WriterId = userId;
@@ -1795,11 +1755,10 @@ namespace Umbraco.Core.Services
if (uow.Events.DispatchCancelable(Copying, this, new CopyEventArgs<IContent>(descendant, descendantCopy, parentId)))
continue;
// a copy is .Saving and will be .Unpublished
// a copy is not published (but not really unpublishing either)
// update the create author and last edit author
// fixme - not like this!
//if (descendantCopy.Published)
// descendantCopy.ChangePublishedState(PublishedState.Unpublished);
if (descendantCopy.Published)
((Content) descendantCopy).Published = false;
descendantCopy.CreatorId = userId;
descendantCopy.WriterId = userId;
@@ -1870,43 +1829,41 @@ namespace Umbraco.Core.Services
/// <returns>The newly created <see cref="IContent"/> object</returns>
public IContent Rollback(int id, Guid versionId, int userId = 0)
{
var content = GetByVersion(versionId);
using (var uow = UowProvider.CreateUnitOfWork())
{
var rollbackEventArgs = new RollbackEventArgs<IContent>(content);
if (uow.Events.DispatchCancelable(RollingBack, this, rollbackEventArgs))
{
uow.Complete();
return content;
}
content.CreatorId = userId;
// need to make sure that the repository is going to save a new version
// but if we're not changing anything, the repository would not save anything
// so - make sure the property IS dirty, doing a flip-flop with an impossible value
content.WriterId = -1;
content.WriterId = userId;
uow.WriteLock(Constants.Locks.ContentTree);
var repository = uow.CreateRepository<IContentRepository>();
// a rolled back version is .Saving and will be .Unpublished
// fixme - not like this!
//content.ChangePublishedState(PublishedState.Unpublished);
var currContent = repository.Get(id);
var origContent = repository.GetByVersion(versionId);
repository.AddOrUpdate(content);
var rollbackEventArgs = new RollbackEventArgs<IContent>(origContent);
if (uow.Events.DispatchCancelable(RollingBack, this, rollbackEventArgs))
{
uow.Complete();
return origContent;
}
((Content) currContent).RollbackAllValues(origContent);
currContent.WriterId = userId;
// publish name is always the current publish name
// but name is the actual version name, this is what we want
currContent.Name = origContent.Name;
currContent.Template = origContent.Template;
// save the values
repository.AddOrUpdate(currContent);
rollbackEventArgs.CanCancel = false;
uow.Events.Dispatch(RolledBack, this, rollbackEventArgs);
uow.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(content, TreeChangeTypes.RefreshNode).ToEventArgs());
Audit(uow, AuditType.RollBack, "Content rollback performed by user", content.WriterId, content.Id);
uow.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(currContent, TreeChangeTypes.RefreshNode).ToEventArgs());
Audit(uow, AuditType.RollBack, "Content rollback performed by user", currContent.WriterId, currContent.Id);
uow.Complete();
}
return content;
return currContent;
}
}
/// <summary>
@@ -1957,8 +1914,6 @@ namespace Umbraco.Core.Services
// since we're not really publishing it and it cannot be cancelled etc
if (content.Published)
published.Add(content);
else if (content.HasPublishedVersion)
published.Add(GetByVersion(content.PublishedVersionGuid));
// save
saved.Add(content);
@@ -2112,7 +2067,7 @@ namespace Umbraco.Core.Services
var newest = GetById(content.Id); // ensure we have the newest version
if (content.Version != newest.Version) // but use the original object if it's already the newest version
content = newest;
if (content.Published == false && content.HasPublishedVersion == false)
if (content.Published == false)
{
uow.Complete();
return Attempt.Succeed(new UnPublishStatus(UnPublishedStatusType.SuccessAlreadyUnPublished, evtMsgs, content)); // already unpublished
@@ -2161,7 +2116,7 @@ namespace Umbraco.Core.Services
var isNew = content.IsNewEntity();
var changeType = isNew ? TreeChangeTypes.RefreshBranch : TreeChangeTypes.RefreshNode;
var previouslyPublished = content.HasIdentity && content.HasPublishedVersion;
var previouslyPublished = content.HasIdentity && content.Published;
uow.WriteLock(Constants.Locks.ContentTree);
var repository = uow.CreateRepository<IContentRepository>();
@@ -2179,6 +2134,10 @@ namespace Umbraco.Core.Services
content.CreatorId = userId;
content.WriterId = userId;
// fixme - this should be done OUTSIDE the service
// but for the time being... it needs to be done
((Content) content).PublishAllValues();
repository.AddOrUpdate(content);
if (raiseEvents) // always
@@ -2415,23 +2374,7 @@ namespace Umbraco.Core.Services
// else check ancestors - we know we are not trashed
if (pathIsOk == false)
{
// ensure all ancestors are published
// because content may be new its Path may be null - start with parent
var path = content.Path ?? content.Parent(this).Path;
if (path != null) // if parent is also null, give up
{
var ancestorIds = path.Split(',')
.Skip(1) // remove leading "-1"
.Reverse()
.Select(int.Parse);
if (content.Path != null)
ancestorIds = ancestorIds.Skip(1); // remove trailing content.Id
if (ancestorIds.All(HasPublishedVersion))
pathIsOk = true;
}
}
pathIsOk = IsPathPublished(content.Parent());
if (pathIsOk == false)
{
@@ -2521,24 +2464,6 @@ namespace Umbraco.Core.Services
continue;
}
if (content.HasPublishedVersion)
{
// newest is published already but we are topLevel, or
// newest is not published, but another version is - publish newest
var r = StrategyPublish(uow, content, alreadyCheckedA.Contains(content), userId, evtMsgs);
if (r.Success == false)
{
// we tried to publish and it failed, but it already had / still has a published version,
// the rule in remarks says that we should skip the underlying branch if includeUnpublished
// is false, else process it - not that it makes much sense, but keep it like that for now
if (includeUnpublished == false)
excude.Add(content.Id);
}
statuses.Add(r);
continue;
}
if (content.Level == topLevel || includeUnpublished)
{
// content has no published version, and we want to publish it, either
@@ -2658,7 +2583,7 @@ namespace Umbraco.Core.Services
// if it's not trashed yet, and published, we should unpublish
// but... UnPublishing event makes no sense (not going to cancel?) and no need to save
// just raise the event
if (content.Trashed == false && content.HasPublishedVersion)
if (content.Trashed == false && content.Published)
uow.Events.Dispatch(UnPublished, this, new PublishEventArgs<IContent>(content, false, false), "UnPublished");
// if current content has children, move them to trash
@@ -2741,7 +2666,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IContentBlueprintRepository>();
var blueprint = repository.Get(id);
if (blueprint != null)
((Content) blueprint).IsBlueprint = true;
((Content) blueprint).Blueprint = true;
return blueprint;
}
}
@@ -2754,7 +2679,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IContentBlueprintRepository>();
var blueprint = repository.Get(id);
if (blueprint != null)
((Content) blueprint).IsBlueprint = true;
((Content) blueprint).Blueprint = true;
return blueprint;
}
}
@@ -2765,7 +2690,7 @@ namespace Umbraco.Core.Services
if (content.ParentId != -1)
content.ParentId = -1;
((Content) content).IsBlueprint = true;
((Content) content).Blueprint = true;
using (var uow = UowProvider.CreateUnitOfWork())
{
@@ -2834,7 +2759,7 @@ namespace Umbraco.Core.Services
}
return repository.GetByQuery(query).Select(x =>
{
((Content) x).IsBlueprint = true;
((Content) x).Blueprint = true;
return x;
});
}
+109 -153
View File
@@ -3,13 +3,9 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Services
@@ -22,141 +18,122 @@ namespace Umbraco.Core.Services
internal class EntityXmlSerializer
{
/// <summary>
/// Exports an <see cref="IContent"/> item to xml as an <see cref="XElement"/>
/// Exports an IContent item as an XElement.
/// </summary>
/// <param name="contentService"></param>
/// <param name="dataTypeService"></param>
/// <param name="userService"></param>
/// <param name="content">Content to export</param>
/// <param name="urlSegmentProviders"></param>
/// <param name="deep">Optional parameter indicating whether to include descendents</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Content object</returns>
public XElement Serialize(
public static XElement Serialize(
IContentService contentService,
IDataTypeService dataTypeService,
IUserService userService,
ILocalizationService localizationService,
IEnumerable<IUrlSegmentProvider> urlSegmentProviders,
IContent content,
bool deep = false)
bool published,
bool withDescendants = false) // fixme take care of usage!
{
if (contentService == null) throw new ArgumentNullException("contentService");
if (dataTypeService == null) throw new ArgumentNullException("dataTypeService");
if (userService == null) throw new ArgumentNullException("userService");
if (content == null) throw new ArgumentNullException("content");
if (urlSegmentProviders == null) throw new ArgumentNullException("urlSegmentProviders");
//nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
if (contentService == null) throw new ArgumentNullException(nameof(contentService));
if (dataTypeService == null) throw new ArgumentNullException(nameof(dataTypeService));
if (userService == null) throw new ArgumentNullException(nameof(userService));
if (localizationService == null) throw new ArgumentNullException(nameof(localizationService));
if (content == null) throw new ArgumentNullException(nameof(content));
if (urlSegmentProviders == null) throw new ArgumentNullException(nameof(urlSegmentProviders));
// nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
var nodeName = content.ContentType.Alias.ToSafeAliasWithForcingCheck();
var xml = Serialize(dataTypeService, content, content.GetUrlSegment(urlSegmentProviders), nodeName);
var xml = SerializeContentBase(dataTypeService, localizationService, content, content.GetUrlSegment(urlSegmentProviders), nodeName, published);
xml.Add(new XAttribute("nodeType", content.ContentType.Id));
xml.Add(new XAttribute("creatorName", content.GetCreatorProfile(userService).Name));
xml.Add(new XAttribute("writerName", content.GetWriterProfile(userService).Name));
xml.Add(new XAttribute("writerID", content.WriterId));
xml.Add(new XAttribute("template", content.Template == null ? "0" : content.Template.Id.ToString(CultureInfo.InvariantCulture)));
xml.Add(new XAttribute("nodeTypeAlias", content.ContentType.Alias));
if (deep)
xml.Add(new XAttribute("creatorName", content.GetCreatorProfile(userService).Name));
//xml.Add(new XAttribute("creatorID", content.CreatorId));
xml.Add(new XAttribute("writerName", content.GetWriterProfile(userService).Name));
xml.Add(new XAttribute("writerID", content.WriterId));
xml.Add(new XAttribute("template", content.Template?.Id.ToString(CultureInfo.InvariantCulture) ?? "0"));
if (withDescendants)
{
var descendants = contentService.GetDescendants(content).ToArray();
var currentChildren = descendants.Where(x => x.ParentId == content.Id);
AddChildXml(contentService, dataTypeService, userService, urlSegmentProviders, descendants, currentChildren, xml);
SerializeDescendants(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, descendants, currentChildren, xml, published);
}
return xml;
}
/// <summary>
/// Exports an <see cref="IMedia"/> item to xml as an <see cref="XElement"/>
/// Exports an IMedia item as an XElement.
/// </summary>
/// <param name="mediaService"></param>
/// <param name="dataTypeService"></param>
/// <param name="userService"></param>
/// <param name="media">Media to export</param>
/// <param name="urlSegmentProviders"></param>
/// <param name="deep">Optional parameter indicating whether to include descendents</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Media object</returns>
public XElement Serialize(
public static XElement Serialize(
IMediaService mediaService,
IDataTypeService dataTypeService,
IUserService userService,
ILocalizationService localizationService,
IEnumerable<IUrlSegmentProvider> urlSegmentProviders,
IMedia media,
bool deep = false)
bool withDescendants = false)
{
if (mediaService == null) throw new ArgumentNullException("mediaService");
if (dataTypeService == null) throw new ArgumentNullException("dataTypeService");
if (userService == null) throw new ArgumentNullException("userService");
if (media == null) throw new ArgumentNullException("media");
if (urlSegmentProviders == null) throw new ArgumentNullException("urlSegmentProviders");
//nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
if (mediaService == null) throw new ArgumentNullException(nameof(mediaService));
if (dataTypeService == null) throw new ArgumentNullException(nameof(dataTypeService));
if (userService == null) throw new ArgumentNullException(nameof(userService));
if (localizationService == null) throw new ArgumentNullException(nameof(localizationService));
if (media == null) throw new ArgumentNullException(nameof(media));
if (urlSegmentProviders == null) throw new ArgumentNullException(nameof(urlSegmentProviders));
// nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
var nodeName = media.ContentType.Alias.ToSafeAliasWithForcingCheck();
var xml = Serialize(dataTypeService, media, media.GetUrlSegment(urlSegmentProviders), nodeName);
const bool published = false; // always false for media
var xml = SerializeContentBase(dataTypeService, localizationService, media, media.GetUrlSegment(urlSegmentProviders), nodeName, published);
xml.Add(new XAttribute("nodeType", media.ContentType.Id));
xml.Add(new XAttribute("writerName", media.GetCreatorProfile(userService).Name));
xml.Add(new XAttribute("writerID", media.CreatorId));
xml.Add(new XAttribute("version", media.Version));
xml.Add(new XAttribute("template", 0));
xml.Add(new XAttribute("nodeTypeAlias", media.ContentType.Alias));
if (deep)
//xml.Add(new XAttribute("creatorName", media.GetCreatorProfile(userService).Name));
//xml.Add(new XAttribute("creatorID", media.CreatorId));
xml.Add(new XAttribute("writerName", media.GetWriterProfile(userService).Name));
xml.Add(new XAttribute("writerID", media.WriterId));
xml.Add(new XAttribute("version", media.Version));
//xml.Add(new XAttribute("template", 0)); // no template for media
if (withDescendants)
{
var descendants = mediaService.GetDescendants(media).ToArray();
var currentChildren = descendants.Where(x => x.ParentId == media.Id);
AddChildXml(mediaService, dataTypeService, userService, urlSegmentProviders, descendants, currentChildren, xml);
SerializeDescendants(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, descendants, currentChildren, xml);
}
return xml;
}
/// <summary>
/// Exports an <see cref="IMember"/> item to xml as an <see cref="XElement"/>
/// Exports an IMember item as an XElement.
/// </summary>
/// <param name="dataTypeService"></param>
/// <param name="member">Member to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Member object</returns>
public XElement Serialize(IDataTypeService dataTypeService, IMember member)
public static XElement Serialize(
IDataTypeService dataTypeService,
ILocalizationService localizationService,
IMember member)
{
//nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
// nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
var nodeName = member.ContentType.Alias.ToSafeAliasWithForcingCheck();
var xml = Serialize(dataTypeService, member, "", nodeName);
const bool published = false; // always false for member
var xml = SerializeContentBase(dataTypeService, localizationService, member, "", nodeName, published);
xml.Add(new XAttribute("nodeType", member.ContentType.Id));
xml.Add(new XAttribute("nodeTypeAlias", member.ContentType.Alias));
// what about writer/creator/version?
xml.Add(new XAttribute("loginName", member.Username));
xml.Add(new XAttribute("email", member.Email));
xml.Add(new XAttribute("icon", member.ContentType.Icon));
return xml;
}
public XElement Serialize(IDataTypeService dataTypeService, Property property)
{
var propertyType = property.PropertyType;
var nodeName = property.Alias.ToSafeAlias();
var xElement = new XElement(nodeName);
//Get the property editor for thsi property and let it convert it to the xml structure
var propertyEditor = Current.PropertyEditors[property.PropertyType.PropertyEditorAlias];
if (propertyEditor != null)
{
var xmlValue = propertyEditor.ValueEditor.ConvertDbToXml(property, propertyType, dataTypeService);
xElement.Add(xmlValue);
}
return xElement;
}
/// <summary>
/// Exports an <see cref="IDataTypeDefinition"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dataTypeService"></param>
/// <param name="dataTypeDefinition">IDataTypeDefinition type to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition object</returns>
public XElement Serialize(IDataTypeService dataTypeService, IDataTypeDefinition dataTypeDefinition)
{
var prevalues = new XElement("PreValues");
@@ -251,11 +228,10 @@ namespace Umbraco.Core.Services
xml.Add(new XElement("Alias", template.Alias));
xml.Add(new XElement("Design", new XCData(template.Content)));
var concreteTemplate = template as Template;
if (concreteTemplate != null && concreteTemplate.MasterTemplateId != null)
if (template is Template concreteTemplate && concreteTemplate.MasterTemplateId != null)
{
if (concreteTemplate.MasterTemplateId.IsValueCreated &&
concreteTemplate.MasterTemplateId.Value != default(int))
concreteTemplate.MasterTemplateId.Value != default)
{
xml.Add(new XElement("Master", concreteTemplate.MasterTemplateId.ToString()));
xml.Add(new XElement("MasterAlias", concreteTemplate.MasterTemplateAlias));
@@ -355,13 +331,6 @@ namespace Umbraco.Core.Services
return xml;
}
/// <summary>
/// Exports an <see cref="IContentType"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dataTypeService"></param>
/// <param name="contentTypeService"></param>
/// <param name="contentType">Content type to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IContentType object</returns>
public XElement Serialize(IDataTypeService dataTypeService, IContentTypeService contentTypeService, IContentType contentType)
{
var info = new XElement("Info",
@@ -460,41 +429,8 @@ namespace Umbraco.Core.Services
return xml;
}
/// <summary>
/// Used by Media Export to recursively add children
/// </summary>
/// <param name="mediaService"></param>
/// <param name="dataTypeService"></param>
/// <param name="userService"></param>
/// <param name="urlSegmentProviders"></param>
/// <param name="originalDescendants"></param>
/// <param name="currentChildren"></param>
/// <param name="currentXml"></param>
private void AddChildXml(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, IEnumerable<IUrlSegmentProvider> urlSegmentProviders, IMedia[] originalDescendants, IEnumerable<IMedia> currentChildren, XElement currentXml)
{
foreach (var child in currentChildren)
{
//add the child's xml
var childXml = Serialize(mediaService, dataTypeService, userService, urlSegmentProviders, child);
currentXml.Add(childXml);
//copy local (out of closure)
var c = child;
//get this item's children
var children = originalDescendants.Where(x => x.ParentId == c.Id);
//recurse and add it's children to the child xml element
AddChildXml(mediaService, dataTypeService, userService, urlSegmentProviders, originalDescendants, children, childXml);
}
}
/// <summary>
/// Part of the export of IContent and IMedia and IMember which is shared
/// </summary>
/// <param name="dataTypeService"></param>
/// <param name="contentBase">Base Content or Media to export</param>
/// <param name="urlValue"></param>
/// <param name="nodeName">Name of the node</param>
/// <returns><see cref="XElement"/></returns>
private XElement Serialize(IDataTypeService dataTypeService, IContentBase contentBase, string urlValue, string nodeName)
// exports an IContentBase (IContent, IMedia or IMember) as an XElement.
private static XElement SerializeContentBase(IDataTypeService dataTypeService, ILocalizationService localizationService, IContentBase contentBase, string urlValue, string nodeName, bool published)
{
var xml = new XElement(nodeName,
new XAttribute("id", contentBase.Id),
@@ -510,37 +446,57 @@ namespace Umbraco.Core.Services
new XAttribute("path", contentBase.Path),
new XAttribute("isDoc", ""));
foreach (var property in contentBase.Properties.Where(p => p != null && p.GetValue() != null && p.GetValue().ToString().IsNullOrWhiteSpace() == false))
{
xml.Add(Serialize(dataTypeService, property));
}
foreach (var property in contentBase.Properties)
xml.Add(SerializeProperty(dataTypeService, localizationService, property, published));
return xml;
}
/// <summary>
/// Used by Content Export to recursively add children
/// </summary>
/// <param name="contentService"></param>
/// <param name="dataTypeService"></param>
/// <param name="userService"></param>
/// <param name="urlSegmentProviders"></param>
/// <param name="originalDescendants"></param>
/// <param name="currentChildren"></param>
/// <param name="currentXml"></param>
private void AddChildXml(IContentService contentService, IDataTypeService dataTypeService, IUserService userService, IEnumerable<IUrlSegmentProvider> urlSegmentProviders, IContent[] originalDescendants, IEnumerable<IContent> currentChildren, XElement currentXml)
// exports a property as XElements.
private static IEnumerable<XElement> SerializeProperty(IDataTypeService dataTypeService, ILocalizationService localizationService, Property property, bool published)
{
foreach (var child in currentChildren)
var propertyType = property.PropertyType;
// get the property editor for this property and let it convert it to the xml structure
var propertyEditor = Current.PropertyEditors[propertyType.PropertyEditorAlias];
return propertyEditor == null
? Array.Empty<XElement>()
: propertyEditor.ValueEditor.ConvertDbToXml(property, dataTypeService, localizationService, published);
}
// exports an IContent item descendants.
private static void SerializeDescendants(IContentService contentService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable<IUrlSegmentProvider> urlSegmentProviders, IContent[] originalDescendants, IEnumerable<IContent> children, XElement xml, bool published)
{
foreach (var child in children)
{
//add the child's xml
var childXml = Serialize(contentService, dataTypeService, userService, urlSegmentProviders, child);
currentXml.Add(childXml);
//copy local (out of closure)
var c = child;
//get this item's children
var children = originalDescendants.Where(x => x.ParentId == c.Id);
//recurse and add it's children to the child xml element
AddChildXml(contentService, dataTypeService, userService, urlSegmentProviders, originalDescendants, children, childXml);
// add the child xml
var childXml = Serialize(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, child, published);
xml.Add(childXml);
// capture id (out of closure) and get the grandChildren (children of the child)
var parentId = child.Id;
var grandChildren = originalDescendants.Where(x => x.ParentId == parentId);
// recurse
SerializeDescendants(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, originalDescendants, grandChildren, childXml, published);
}
}
// exports an IMedia item descendants.
private static void SerializeDescendants(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable<IUrlSegmentProvider> urlSegmentProviders, IMedia[] originalDescendants, IEnumerable<IMedia> children, XElement xml)
{
foreach (var child in children)
{
// add the child xml
var childXml = Serialize(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, child);
xml.Add(childXml);
// capture id (out of closure) and get the grandChildren (children of the child)
var parentId = child.Id;
var grandChildren = originalDescendants.Where(x => x.ParentId == parentId);
// recurse
SerializeDescendants(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, originalDescendants, grandChildren, childXml);
}
}
}
@@ -428,20 +428,6 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IContent"/> item</returns>
IContent GetByVersion(Guid versionId);
/// <summary>
/// Gets the published version of an <see cref="IContent"/> item
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/> to retrieve version from</param>
/// <returns>An <see cref="IContent"/> item</returns>
IContent GetPublishedVersion(int id);
/// <summary>
/// Gets the published version of a <see cref="IContent"/> item.
/// </summary>
/// <param name="content">The content item.</param>
/// <returns>The published version, if any; otherwise, null.</returns>
IContent GetPublishedVersion(IContent content);
/// <summary>
/// Checks whether an <see cref="IContent"/> item has any children
/// </summary>
@@ -449,13 +435,6 @@ namespace Umbraco.Core.Services
/// <returns>True if the content has any children otherwise False</returns>
bool HasChildren(int id);
/// <summary>
/// Checks whether an <see cref="IContent"/> item has any published versions
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/></param>
/// <returns>True if the content has any published version otherwise False</returns>
bool HasPublishedVersion(int id);
/// <summary>
/// Publishes a single <see cref="IContent"/> object
/// </summary>
+1 -1
View File
@@ -1228,7 +1228,7 @@ namespace Umbraco.Core.Services
var identity = int.MaxValue;
var memType = new MemberType(-1);
var propGroup = new PropertyGroup
var propGroup = new PropertyGroup(MemberType.IsPublishingConst)
{
Name = "Membership",
Id = --identity
@@ -98,8 +98,8 @@ namespace Umbraco.Core.Services
return new XElement(nodeName);
}
var exporter = new EntityXmlSerializer();
var xml = exporter.Serialize(_contentService, _dataTypeService, _userService, _urlSegmentProviders, content, deep);
const bool published = false; // fixme - what shall we export?
var xml = EntityXmlSerializer.Serialize(_contentService, _dataTypeService, _userService, _localizationService, _urlSegmentProviders, content, published, deep);
if (raiseEvents)
ExportedContent.RaiseEvent(new ExportEventArgs<IContent>(content, xml, false), this);
@@ -1408,8 +1408,7 @@ namespace Umbraco.Core.Services
/// <returns><see cref="XElement"/> containing the xml representation of the Member object</returns>
public XElement Export(IMember member)
{
var exporter = new EntityXmlSerializer();
return exporter.Serialize(_dataTypeService, member);
return EntityXmlSerializer.Serialize(_dataTypeService, _localizationService, member);
}
#endregion
@@ -1433,8 +1432,7 @@ namespace Umbraco.Core.Services
return new XElement(nodeName);
}
var exporter = new EntityXmlSerializer();
var xml = exporter.Serialize(_mediaService, _dataTypeService, _userService, _urlSegmentProviders, media, deep);
var xml = EntityXmlSerializer.Serialize(_mediaService, _dataTypeService, _userService, _localizationService, _urlSegmentProviders, media, deep);
if (raiseEvents)
ExportedMedia.RaiseEvent(new ExportEventArgs<IMedia>(media, xml, false), this);
+8 -8
View File
@@ -72,8 +72,8 @@ namespace Umbraco.Core.Services
/// <param name="replaceTags">Whether or not to replace the tags with the new value or append them (true to replace, false to append)</param>
/// <param name="tagGroup">The tag group to use when tagging</param>
/// <param name="valueType">Defines how the tag values will be extracted</param>
/// <param name="storageType">Defines how to store the tags in cache (CSV or Json)</param>
internal static void SetPropertyTags(Property property, object convertedPropertyValue, string delimiter, bool replaceTags, string tagGroup, TagValueType valueType, TagCacheStorageType storageType)
/// <param name="storage">Defines how to store the tags in cache (CSV or Json)</param>
internal static void SetPropertyTags(Property property, object convertedPropertyValue, string delimiter, bool replaceTags, string tagGroup, TagValueType valueType, TagCacheStorageType storage)
{
if (convertedPropertyValue == null)
{
@@ -84,24 +84,24 @@ namespace Umbraco.Core.Services
{
case TagValueType.FromDelimitedValue:
var tags = convertedPropertyValue.ToString().Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
property.SetTags(storageType, property.Alias, tags, replaceTags, tagGroup);
property.SetTags(property.Alias, tags, replaceTags, tagGroup, storage);
break;
case TagValueType.CustomTagList:
//for this to work the object value must be IENumerable<string>
var stringList = convertedPropertyValue as IEnumerable<string>;
if (stringList != null)
{
property.SetTags(storageType, property.Alias, stringList, replaceTags, tagGroup);
property.SetTags(property.Alias, stringList, replaceTags, tagGroup, storage);
}
else
{
//it's not enumerable string, so lets see if we can automatically make it that way based on the current storage type
switch (storageType)
switch (storage)
{
case TagCacheStorageType.Csv:
var split = convertedPropertyValue.ToString().Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
//recurse with new value
SetPropertyTags(property, split, delimiter, replaceTags, tagGroup, valueType, storageType);
SetPropertyTags(property, split, delimiter, replaceTags, tagGroup, valueType, storage);
break;
case TagCacheStorageType.Json:
try
@@ -110,7 +110,7 @@ namespace Umbraco.Core.Services
if (parsedJson != null)
{
//recurse with new value
SetPropertyTags(property, parsedJson, delimiter, replaceTags, tagGroup, valueType, storageType);
SetPropertyTags(property, parsedJson, delimiter, replaceTags, tagGroup, valueType, storage);
}
}
catch (Exception ex)
@@ -119,7 +119,7 @@ namespace Umbraco.Core.Services
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(storageType));
throw new ArgumentOutOfRangeException(nameof(storage));
}
}
break;
+1 -1
View File
@@ -132,7 +132,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IContent entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(entity.IsBlueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, entity.Key).EnsureClosed();
return new GuidUdi(entity.Blueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, entity.Key).EnsureClosed();
}
/// <summary>
+1 -3
View File
@@ -636,11 +636,9 @@
<Compile Include="Models\PreValueCollection.cs" />
<Compile Include="Models\Property.cs" />
<Compile Include="Models\PropertyCollection.cs" />
<Compile Include="Models\PropertyExtensions.cs" />
<Compile Include="Models\PropertyGroup.cs" />
<Compile Include="Models\PropertyGroupCollection.cs" />
<Compile Include="Models\PropertyTagBehavior.cs" />
<Compile Include="Models\PropertyTags.cs" />
<Compile Include="Models\PropertyTagChange.cs" />
<Compile Include="Models\PropertyType.cs" />
<Compile Include="Models\PropertyTypeCollection.cs" />
<Compile Include="Models\PublicAccessEntry.cs" />
@@ -70,7 +70,7 @@ namespace Umbraco.Tests.Benchmarks
var template = SqlTemplates.Get("test", s => s
.Select<NPocoFetchTests.Thing1Dto>()
.From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.ArgValue<string>("name")));
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("name")));
var sql = template.Sql(new { name = "yada" });
@@ -40,11 +40,6 @@ namespace Umbraco.Tests.Integration
// ensure there's a current context
GetUmbracoContext("http://www.example.com/", 0, null, true);
// prepare content type
_contentType = MockedContentTypes.CreateSimpleContentType("whatever", "Whatever");
_contentType.Key = Guid.NewGuid();
ServiceContext.ContentTypeService.Save(_contentType);
}
protected override void Compose()
@@ -60,6 +55,17 @@ namespace Umbraco.Tests.Integration
.Add<MacroCacheRefresher>();
}
protected override void Initialize()
{
base.Initialize();
// prepare content type
_contentType = MockedContentTypes.CreateSimpleContentType("whatever", "Whatever");
_contentType.Key = Guid.NewGuid();
ServiceContext.FileService.SaveTemplate(_contentType.DefaultTemplate);
ServiceContext.ContentTypeService.Save(_contentType);
}
public override void TearDown()
{
base.TearDown();
@@ -216,7 +222,7 @@ namespace Umbraco.Tests.Integration
{
state += "x";
}
else if (x.HasPublishedVersion)
else if (x.Published)
{
var isPathPublished = ((ContentRepository)sender).IsPathPublished(x); // expensive!
if (isPathPublished == false)
@@ -59,7 +59,7 @@ namespace Umbraco.Tests.Models.Collections
public void PropertyTypeCollection_Returns_Null_On_FirstOrDefault_When_Empty()
{
var list = new List<PropertyType>();
var collection = new PropertyTypeCollection(list);
var collection = new PropertyTypeCollection(false, list);
Assert.That(collection.FirstOrDefault(), Is.Null);
Assert.That(collection.FirstOrDefault(x => x.Alias.InvariantEquals("Test")) == null, Is.True);
+7 -7
View File
@@ -455,7 +455,7 @@ namespace Umbraco.Tests.Models
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
// Act
contentType.PropertyGroups.Add(new PropertyGroup{ Name = "Test Group", SortOrder = 3 });
contentType.PropertyGroups.Add(new PropertyGroup(true) { Name = "Test Group", SortOrder = 3 });
// Assert
Assert.That(contentType.PropertyGroups.Count, Is.EqualTo(3));
@@ -537,7 +537,7 @@ namespace Umbraco.Tests.Models
SortOrder = 3,
DataTypeDefinitionId = -88
};
var propertyGroup = new PropertyGroup {Name = "Test Group", SortOrder = 3};
var propertyGroup = new PropertyGroup(true) { Name = "Test Group", SortOrder = 3};
propertyGroup.PropertyTypes.Add(propertyType);
contentType.PropertyGroups.Add(propertyGroup);
var newProperty = new Property(propertyType);
@@ -685,7 +685,7 @@ namespace Umbraco.Tests.Models
contentType.ResetDirtyProperties();
// Act
var propertyGroup = new PropertyGroup { Name = "Test Group", SortOrder = 3 };
var propertyGroup = new PropertyGroup(true) { Name = "Test Group", SortOrder = 3 };
contentType.PropertyGroups.Add(propertyGroup);
// Assert
@@ -800,7 +800,7 @@ namespace Umbraco.Tests.Models
// Arrange
var simpleContentType = MockedContentTypes.CreateSimpleContentType();
var simple2ContentType = MockedContentTypes.CreateSimpleContentType("anotherSimple", "Another Simple Page",
new PropertyTypeCollection(
new PropertyTypeCollection(true,
new List<PropertyType>
{
new PropertyType("test", DataTypeDatabaseType.Ntext, "coauthor")
@@ -831,7 +831,7 @@ namespace Umbraco.Tests.Models
var metaContentType = MockedContentTypes.CreateMetaContentType();
var simpleContentType = MockedContentTypes.CreateSimpleContentType();
var simple2ContentType = MockedContentTypes.CreateSimpleContentType("anotherSimple", "Another Simple Page",
new PropertyTypeCollection(
new PropertyTypeCollection(true,
new List<PropertyType>
{
new PropertyType("test", DataTypeDatabaseType.Ntext, "coauthor")
@@ -864,7 +864,7 @@ namespace Umbraco.Tests.Models
var textPage = MockedContentTypes.CreateTextpageContentType();
var parent = MockedContentTypes.CreateSimpleContentType("parent", "Parent", null, true);
var meta = MockedContentTypes.CreateMetaContentType();
var mixin1 = MockedContentTypes.CreateSimpleContentType("mixin1", "Mixin1", new PropertyTypeCollection(
var mixin1 = MockedContentTypes.CreateSimpleContentType("mixin1", "Mixin1", new PropertyTypeCollection(true,
new List<PropertyType>
{
new PropertyType("test", DataTypeDatabaseType.Ntext, "coauthor")
@@ -876,7 +876,7 @@ namespace Umbraco.Tests.Models
DataTypeDefinitionId = -88
}
}));
var mixin2 = MockedContentTypes.CreateSimpleContentType("mixin2", "Mixin2", new PropertyTypeCollection(
var mixin2 = MockedContentTypes.CreateSimpleContentType("mixin2", "Mixin2", new PropertyTypeCollection(true,
new List<PropertyType>
{
new PropertyType("test", DataTypeDatabaseType.Ntext, "author")
+1 -1
View File
@@ -63,7 +63,7 @@ namespace Umbraco.Tests.Models
Assert.AreEqual(media.GetCreatorProfile(ServiceContext.UserService).Name, (string)element.Attribute("writerName"));
Assert.AreEqual(media.CreatorId.ToString(), (string)element.Attribute("writerID"));
Assert.AreEqual(media.Version.ToString(), (string)element.Attribute("version"));
Assert.AreEqual("0", (string)element.Attribute("template"));
Assert.IsNull(element.Attribute("template"));
Assert.AreEqual(media.Properties[Constants.Conventions.Media.File].GetValue().ToString(), element.Elements(Constants.Conventions.Media.File).Single().Value);
Assert.AreEqual(media.Properties[Constants.Conventions.Media.Width].GetValue().ToString(), element.Elements(Constants.Conventions.Media.Width).Single().Value);
@@ -14,7 +14,7 @@ namespace Umbraco.Tests.Models
public void Can_Deep_Clone()
{
var pg = new PropertyGroup(
new PropertyTypeCollection(new[]
new PropertyTypeCollection(false, new[]
{
new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar, "test")
{
@@ -91,7 +91,7 @@ namespace Umbraco.Tests.Models
var ss = new SerializationService(new JsonNetSerializer());
var pg = new PropertyGroup(
new PropertyTypeCollection(new[]
new PropertyTypeCollection(false, new[]
{
new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar, "test")
{
@@ -170,9 +170,8 @@ namespace Umbraco.Tests.Models
ContentTypeIcon = "icon",
ContentTypeThumbnail = "thumb",
HasChildren = true,
HasPendingChanges = true,
IsDraft = true,
IsPublished = true,
Edited = true,
Published = true,
NodeObjectTypeId = Guid.NewGuid()
};
item.AdditionalData.Add("test1", 3);
@@ -206,9 +205,8 @@ namespace Umbraco.Tests.Models
Assert.AreEqual(clone.ContentTypeIcon, item.ContentTypeIcon);
Assert.AreEqual(clone.ContentTypeThumbnail, item.ContentTypeThumbnail);
Assert.AreEqual(clone.HasChildren, item.HasChildren);
Assert.AreEqual(clone.HasPendingChanges, item.HasPendingChanges);
Assert.AreEqual(clone.IsDraft, item.IsDraft);
Assert.AreEqual(clone.IsPublished, item.IsPublished);
Assert.AreEqual(clone.Edited, item.Edited);
Assert.AreEqual(clone.Published, item.Published);
Assert.AreEqual(clone.NodeObjectTypeId, item.NodeObjectTypeId);
Assert.AreEqual(clone.UpdateDate, item.UpdateDate);
Assert.AreEqual(clone.AdditionalData.Count, item.AdditionalData.Count);
@@ -243,9 +241,8 @@ namespace Umbraco.Tests.Models
ContentTypeIcon = "icon",
ContentTypeThumbnail = "thumb",
HasChildren = true,
HasPendingChanges = true,
IsDraft = true,
IsPublished = true,
Edited = true,
Published = true,
NodeObjectTypeId = Guid.NewGuid()
};
item.AdditionalData.Add("test1", 3);
@@ -50,7 +50,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests
const string sqlBase = "SELECT [zbThing1].[id] AS [Id], [zbThing1].[name] AS [Name] FROM [zbThing1] WHERE ";
var template = sqlTemplates.Get("sql1", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.ArgValue<string>("value")));
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("value")));
var sql = template.Sql("foo");
Assert.AreEqual(sqlBase + "(([zbThing1].[name] = @0))", sql.SQL.NoCrLf());
@@ -63,7 +63,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests
Assert.AreEqual(123, sql.Arguments[0]);
template = sqlTemplates.Get("sql2", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.ArgValue<string>("value")));
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("value")));
sql = template.Sql(new { value = "foo" });
Assert.AreEqual(sqlBase + "(([zbThing1].[name] = @0))", sql.SQL.NoCrLf());
@@ -97,7 +97,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests
// so we have to explicitely create the argument
template = sqlTemplates.Get("sql4", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == SqlTemplate.ArgValue<int>("i")));
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == SqlTemplate.Arg<int>("i")));
sql = template.Sql("foo");
Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0))", sql.SQL.NoCrLf());
@@ -127,7 +127,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests
// now with more arguments
template = sqlTemplates.Get("sql4a", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == SqlTemplate.ArgValue<int>("i") && x.Name == SqlTemplate.ArgValue<string>("name")));
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == SqlTemplate.Arg<int>("i") && x.Name == SqlTemplate.Arg<string>("name")));
sql = template.Sql(0, 1);
Assert.AreEqual(sqlBase + "((([zbThing1].[id] = @0) AND ([zbThing1].[name] = @1)))", sql.SQL.NoCrLf());
Assert.AreEqual(2, sql.Arguments.Length);
@@ -135,8 +135,8 @@ namespace Umbraco.Tests.Persistence.NPocoTests
Assert.AreEqual(1, sql.Arguments[1]);
template = sqlTemplates.Get("sql4b", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == SqlTemplate.ArgValue<int>("i"))
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.ArgValue<string>("name")));
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == SqlTemplate.Arg<int>("i"))
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("name")));
sql = template.Sql(0, 1);
Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0)) AND (([zbThing1].[name] = @1))", sql.SQL.NoCrLf());
Assert.AreEqual(2, sql.Arguments.Length);
@@ -146,7 +146,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests
// works, magic
template = sqlTemplates.Get("sql5", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.WhereIn<NPocoFetchTests.Thing1Dto>(x => x.Id, SqlTemplate.ArgValueIn<int>("i")));
.WhereIn<NPocoFetchTests.Thing1Dto>(x => x.Id, SqlTemplate.ArgIn<int>("i")));
sql = template.Sql("foo");
Assert.AreEqual(sqlBase + "([zbThing1].[id] IN (@0))", sql.SQL.NoCrLf());
@@ -161,8 +161,8 @@ namespace Umbraco.Tests.Persistence.NPocoTests
Assert.AreEqual(3, sql.Arguments[2]);
template = sqlTemplates.Get("sql5a", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.WhereIn<NPocoFetchTests.Thing1Dto>(x => x.Id, SqlTemplate.ArgValueIn<int>("i"))
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.ArgValue<string>("name")));
.WhereIn<NPocoFetchTests.Thing1Dto>(x => x.Id, SqlTemplate.ArgIn<int>("i"))
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("name")));
sql = template.Sql("foo", "bar");
Assert.AreEqual(sqlBase + "([zbThing1].[id] IN (@0)) AND (([zbThing1].[name] = @1))", sql.SQL.NoCrLf());
@@ -172,7 +172,7 @@ namespace Umbraco.Tests.Persistence.Querying
scope.Database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 66668, UniqueId = 66668.ToGuid(), DataTypeId = 55555, ContentTypeId = 99999, PropertyTypeGroupId = 77778, Alias = "property7", Name = "Property 7", SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null });
scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("cmsPropertyType"))));
ContentTypeRepository.ContentTypeQueryMapper.MapGroupsAndProperties(new[] { 99999 }, scope.Database, SqlSyntax, out allPropTypeCollection, out allPropGroupCollection);
ContentTypeRepository.ContentTypeQueryMapper.MapGroupsAndProperties(new[] { 99999 }, scope.Database, SqlSyntax, true, out allPropTypeCollection, out allPropGroupCollection);
scope.Complete();
}
@@ -352,7 +352,7 @@ namespace Umbraco.Tests.Persistence.Repositories
const string dateTimePropertyAlias = "datetimeProperty";
var dateValue = new DateTime(2016, 1, 6);
var propertyTypeCollection = new PropertyTypeCollection(
var propertyTypeCollection = new PropertyTypeCollection(true,
new List<PropertyType>
{
MockedPropertyTypes.CreateDecimalProperty(decimalPropertyAlias, "Decimal property", dtd.Id),
@@ -24,7 +24,7 @@ namespace Umbraco.Tests.Persistence.Repositories
{
var comparer = new SimilarNodeName.Comparer();
var result = comparer.Compare(new SimilarNodeName { Text = name1 }, new SimilarNodeName { Text = name2 });
var result = comparer.Compare(new SimilarNodeName { Name = name1 }, new SimilarNodeName { Name = name2 });
if (expected == 0)
Assert.AreEqual(0, result);
else if (expected < 0)
@@ -38,16 +38,16 @@ namespace Umbraco.Tests.Persistence.Repositories
{
var names = new[]
{
new SimilarNodeName { Id = 1, Text = "Alpha (2)" },
new SimilarNodeName { Id = 2, Text = "Alpha" },
new SimilarNodeName { Id = 3, Text = "Golf" },
new SimilarNodeName { Id = 4, Text = "Zulu" },
new SimilarNodeName { Id = 5, Text = "Mike" },
new SimilarNodeName { Id = 6, Text = "Kilo (1)" },
new SimilarNodeName { Id = 7, Text = "Yankee" },
new SimilarNodeName { Id = 8, Text = "Kilo" },
new SimilarNodeName { Id = 9, Text = "Golf (2)" },
new SimilarNodeName { Id = 10, Text = "Alpha (1)" },
new SimilarNodeName { Id = 1, Name = "Alpha (2)" },
new SimilarNodeName { Id = 2, Name = "Alpha" },
new SimilarNodeName { Id = 3, Name = "Golf" },
new SimilarNodeName { Id = 4, Name = "Zulu" },
new SimilarNodeName { Id = 5, Name = "Mike" },
new SimilarNodeName { Id = 6, Name = "Kilo (1)" },
new SimilarNodeName { Id = 7, Name = "Yankee" },
new SimilarNodeName { Id = 8, Name = "Kilo" },
new SimilarNodeName { Id = 9, Name = "Golf (2)" },
new SimilarNodeName { Id = 10, Name = "Alpha (1)" },
};
var ordered = names.OrderBy(x => x, new SimilarNodeName.Comparer()).ToArray();
@@ -76,16 +76,16 @@ namespace Umbraco.Tests.Persistence.Repositories
{
var names = new[]
{
new SimilarNodeName { Id = 1, Text = "Alpha (2)" },
new SimilarNodeName { Id = 2, Text = "Alpha" },
new SimilarNodeName { Id = 3, Text = "Golf" },
new SimilarNodeName { Id = 4, Text = "Zulu" },
new SimilarNodeName { Id = 5, Text = "Mike" },
new SimilarNodeName { Id = 6, Text = "Kilo (1)" },
new SimilarNodeName { Id = 7, Text = "Yankee" },
new SimilarNodeName { Id = 8, Text = "Kilo" },
new SimilarNodeName { Id = 9, Text = "Golf (2)" },
new SimilarNodeName { Id = 10, Text = "Alpha (1)" },
new SimilarNodeName { Id = 1, Name = "Alpha (2)" },
new SimilarNodeName { Id = 2, Name = "Alpha" },
new SimilarNodeName { Id = 3, Name = "Golf" },
new SimilarNodeName { Id = 4, Name = "Zulu" },
new SimilarNodeName { Id = 5, Name = "Mike" },
new SimilarNodeName { Id = 6, Name = "Kilo (1)" },
new SimilarNodeName { Id = 7, Name = "Yankee" },
new SimilarNodeName { Id = 8, Name = "Kilo" },
new SimilarNodeName { Id = 9, Name = "Golf (2)" },
new SimilarNodeName { Id = 10, Name = "Alpha (1)" },
};
Assert.AreEqual(expected, SimilarNodeName.GetUniqueName(names, nodeId, nodeName));
@@ -99,7 +99,7 @@ namespace Umbraco.Tests.Persistence
}
[Test]
public void Can_Create_cmsContentVersion_Table()
public void Can_Create_ContentVersion_Table()
{
using (var scope = ScopeProvider.CreateScope())
{
@@ -202,7 +202,7 @@ namespace Umbraco.Tests.Persistence
}
[Test]
public void Can_Create_cmsDocument_Table()
public void Can_Create_Document_Table()
{
using (var scope = ScopeProvider.CreateScope())
{
@@ -219,7 +219,7 @@ namespace Umbraco.Tests.Persistence
}
[Test]
public void Can_Create_cmsDocumentType_Table()
public void Can_Create_DocumentType_Table()
{
using (var scope = ScopeProvider.CreateScope())
{
@@ -35,7 +35,7 @@ namespace Umbraco.Tests.PropertyEditors
var prop = new Property(1, new PropertyType(new DataTypeDefinition(1, "Test.TestEditor")));
prop.SetValue("1234,4567,8910");
var result = editor.ConvertDbToString(prop, prop.PropertyType, new Mock<IDataTypeService>().Object);
var result = editor.ConvertDbToString(prop.PropertyType, prop.GetValue(), new Mock<IDataTypeService>().Object);
Assert.AreEqual("1234,4567,8910", result);
}
@@ -60,7 +60,7 @@ namespace Umbraco.Tests.PropertyEditors
var prop = new Property(1, new PropertyType(new DataTypeDefinition(1, "Test.TestEditor")));
prop.SetValue("1234,4567,8910");
var result = editor.ConvertDbToString(prop, prop.PropertyType, new Mock<IDataTypeService>().Object);
var result = editor.ConvertDbToString(prop.PropertyType, prop.GetValue(), new Mock<IDataTypeService>().Object);
Assert.AreEqual("Value 1,Value 2,Value 3", result);
}
@@ -84,7 +84,7 @@ namespace Umbraco.Tests.PropertyEditors
var prop = new Property(1, new PropertyType(new DataTypeDefinition(1, "Test.TestEditor")));
prop.SetValue("1234");
var result = editor.ConvertDbToString(prop, prop.PropertyType, new Mock<IDataTypeService>().Object);
var result = editor.ConvertDbToString(prop.PropertyType, prop.GetValue(), new Mock<IDataTypeService>().Object);
Assert.AreEqual("Value 2", result);
}
@@ -103,6 +103,7 @@ namespace Umbraco.Tests.Publishing
}
[Test]
[Ignore("cannot work now that publishing has changed")] // fixme redo
public void Publishes_Many_Ignores_Unpublished_Items()
{
CreateTestData();
@@ -132,6 +133,7 @@ namespace Umbraco.Tests.Publishing
}
[Test]
[Ignore("cannot work now that publishing has changed")] // fixme redo
public void Publishes_Many_Does_Not_Ignore_Unpublished_Items()
{
CreateTestData();
+175 -219
View File
@@ -61,6 +61,7 @@ namespace Umbraco.Tests.Services
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateTextpageContentType();
ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate);
contentTypeService.Save(contentType);
var blueprint = MockedContent.CreateTextpageContent(contentType, "hello", -1);
@@ -86,6 +87,7 @@ namespace Umbraco.Tests.Services
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateTextpageContentType();
ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate);
contentTypeService.Save(contentType);
var blueprint = MockedContent.CreateTextpageContent(contentType, "hello", -1);
@@ -109,6 +111,7 @@ namespace Umbraco.Tests.Services
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateTextpageContentType();
ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate);
contentTypeService.Save(contentType);
var blueprint = MockedContent.CreateTextpageContent(contentType, "hello", -1);
@@ -136,8 +139,10 @@ namespace Umbraco.Tests.Services
var contentTypeService = ServiceContext.ContentTypeService;
var ct1 = MockedContentTypes.CreateTextpageContentType("ct1");
ServiceContext.FileService.SaveTemplate(ct1.DefaultTemplate);
contentTypeService.Save(ct1);
var ct2 = MockedContentTypes.CreateTextpageContentType("ct2");
ServiceContext.FileService.SaveTemplate(ct2.DefaultTemplate);
contentTypeService.Save(ct2);
for (int i = 0; i < 10; i++)
@@ -394,6 +399,11 @@ namespace Umbraco.Tests.Services
contentService.MoveToRecycleBin(content1);
// fixme - killing the rest of this test
// this is not working consistently even in 7 when unpublishing a branch
// in 8, tags never go away - one has to check that the entity is published and not trashed
return;
// no more tags for this entity
tags = tagService.GetTagsForEntity(content1.Id);
Assert.AreEqual(0, tags.Count());
@@ -458,6 +468,11 @@ namespace Umbraco.Tests.Services
contentService.UnPublish(content1);
contentService.UnPublish(content2);
// fixme - killing the rest of this test
// this is not working consistently even in 7 when unpublishing a branch
// in 8, tags never go away - one has to check that the entity is published and not trashed
return;
// no more tags
tags = tagService.GetTagsForEntity(content1.Id);
Assert.AreEqual(0, tags.Count());
@@ -590,6 +605,11 @@ namespace Umbraco.Tests.Services
contentService.UnPublish(content1);
contentService.UnPublish(content2);
// fixme - killing the rest of this test
// this is not working consistently even in 7 when unpublishing a branch
// in 8, tags never go away - one has to check that the entity is published and not trashed
return;
var tags = tagService.GetTagsForEntity(content1.Id);
Assert.AreEqual(0, tags.Count());
var allTags = tagService.GetAllContentTags();
@@ -708,35 +728,33 @@ namespace Umbraco.Tests.Services
[Test]
public void Does_Not_Create_Tag_Data_For_Non_Published_Version()
{
//Arrange
var contentService = ServiceContext.ContentService;
var contentTypeService = ServiceContext.ContentTypeService;
// create content type with a tag property
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
DataTypeDefinitionId = 1041
});
contentType.PropertyGroups.First().PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "tags") { DataTypeDefinitionId = 1041 });
contentTypeService.Save(contentType);
// create a content with tags and publish
var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1);
content.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true);
contentService.Publish(content);
// Act
// edit tags and save
content.SetTags("tags", new[] { "another", "world" }, false);
contentService.Save(content);
// Assert
//the value will have changed but the tags db table will not have
// the (edit) property does contain all tags
Assert.AreEqual(5, content.Properties["tags"].GetValue().ToString().Split(',').Distinct().Count());
// but the database still contains the initial two tags
var propertyTypeId = contentType.PropertyTypes.Single(x => x.Alias == "tags").Id;
using (var scope = ScopeProvider.CreateScope())
{
Assert.AreEqual(4, scope.Database.ExecuteScalar<int>(
"SELECT COUNT(*) FROM cmsTagRelationship WHERE nodeId=@nodeId AND propertyTypeId=@propTypeId",
new { nodeId = content.Id, propTypeId = propertyTypeId }));
scope.Complete();
}
}
@@ -935,11 +953,11 @@ namespace Umbraco.Tests.Services
var contentService = ServiceContext.ContentService;
// Act
var content = contentService.GetById(NodeDto.NodeIdSeed + 1);
var content = contentService.GetById(NodeDto.NodeIdSeed + 2 );
// Assert
Assert.That(content, Is.Not.Null);
Assert.That(content.Id, Is.EqualTo(NodeDto.NodeIdSeed + 1));
Assert.That(content.Id, Is.EqualTo(NodeDto.NodeIdSeed + 2));
}
[Test]
@@ -953,7 +971,7 @@ namespace Umbraco.Tests.Services
// Assert
Assert.That(content, Is.Not.Null);
Assert.That(content.Id, Is.EqualTo(NodeDto.NodeIdSeed + 1));
Assert.That(content.Id, Is.EqualTo(NodeDto.NodeIdSeed + 2));
}
[Test]
@@ -978,7 +996,7 @@ namespace Umbraco.Tests.Services
var contentService = ServiceContext.ContentService;
// Act
var contents = contentService.GetChildren(NodeDto.NodeIdSeed + 1).ToList();
var contents = contentService.GetChildren(NodeDto.NodeIdSeed + 2).ToList();
// Assert
Assert.That(contents, Is.Not.Null);
@@ -987,7 +1005,7 @@ namespace Umbraco.Tests.Services
}
[Test]
public void Can_Get_Descendents_Of_Contnet()
public void Can_Get_Descendents_Of_Content()
{
// Arrange
var contentService = ServiceContext.ContentService;
@@ -995,7 +1013,7 @@ namespace Umbraco.Tests.Services
contentService.Save(hierarchy, 0);
// Act
var contents = contentService.GetDescendants(NodeDto.NodeIdSeed + 1).ToList();
var contents = contentService.GetDescendants(NodeDto.NodeIdSeed + 2).ToList();
// Assert
Assert.That(contents, Is.Not.Null);
@@ -1008,31 +1026,37 @@ namespace Umbraco.Tests.Services
{
var contentService = ServiceContext.ContentService;
var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1);
var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 2);
Assert.IsFalse(parent.Published);
ServiceContext.ContentService.Publish(parent); // publishing parent, so Text Page 2 can be updated.
var subpage2 = contentService.GetById(NodeDto.NodeIdSeed + 3);
Assert.IsFalse(subpage2.Published);
var versions = contentService.GetVersions(NodeDto.NodeIdSeed + 3).ToList();
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
Assert.IsFalse(content.Published);
var versions = contentService.GetVersions(NodeDto.NodeIdSeed + 4).ToList();
Assert.AreEqual(1, versions.Count);
// new versions are only added when publishing changes,
// so change values and republish to ensure a version is created
// however because we're not published yet, the first publish will
// NOT create a new version, just publish the current one, so have
// to do it twice!
subpage2.Name = "Text Page 2 Updated";
subpage2.SetValue("author", "Jane Doe");
contentService.SaveAndPublishWithStatus(subpage2); // publishes the current version
subpage2.Name = "Text Page 2 Updated1";
subpage2.SetValue("author", "Bob Hope");
contentService.SaveAndPublishWithStatus(subpage2); // now creates a new version
var version1 = content.Version;
versions = contentService.GetVersions(NodeDto.NodeIdSeed + 3).ToList();
content.Name = "Text Page 2 Updated";
content.SetValue("author", "Jane Doe");
contentService.SaveAndPublishWithStatus(content); // publishes the current version
content.Name = "Text Page 2 ReUpdated";
content.SetValue("author", "Bob Hope");
contentService.SaveAndPublishWithStatus(content); // publishes again, creates a version
var version2 = content.Version;
versions = contentService.GetVersions(NodeDto.NodeIdSeed + 4).ToList();
Assert.AreEqual(2, versions.Count);
Assert.AreEqual("Jane Doe", versions[1].GetValue("author"));
// versions come with most recent first
Assert.AreEqual(version2, versions[0].Version);
Assert.AreEqual(version1, versions[1].Version);
// and proper values
Assert.AreEqual("Bob Hope", versions[0].GetValue("author"));
Assert.AreEqual("Jane Doe", versions[1].GetValue("author", published: true));
}
[Test]
@@ -1055,9 +1079,9 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var root = contentService.GetById(NodeDto.NodeIdSeed + 1);
var root = contentService.GetById(NodeDto.NodeIdSeed + 2);
contentService.SaveAndPublishWithStatus(root);
var content = contentService.GetById(NodeDto.NodeIdSeed + 3);
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
content.ExpireDate = DateTime.Now.AddSeconds(1);
contentService.SaveAndPublishWithStatus(content);
@@ -1107,7 +1131,7 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 1);
var content = contentService.GetById(NodeDto.NodeIdSeed + 2);
bool published = contentService.Publish(content, 0);
var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger);
@@ -1135,7 +1159,7 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 1);
var content = contentService.GetById(NodeDto.NodeIdSeed + 2);
// Act
bool published = contentService.Publish(content, 0);
@@ -1172,7 +1196,7 @@ namespace Umbraco.Tests.Services
try
{
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 1);
var content = contentService.GetById(NodeDto.NodeIdSeed + 2);
Assert.AreEqual("Home", content.Name);
content.Name = "foo";
@@ -1214,7 +1238,7 @@ namespace Umbraco.Tests.Services
contentService.Save(content, 0);
// Act
var parent = contentService.GetById(NodeDto.NodeIdSeed + 1);
var parent = contentService.GetById(NodeDto.NodeIdSeed + 2);
bool parentPublished = contentService.Publish(parent, 0);
bool published = contentService.Publish(content, 0);
@@ -1231,16 +1255,16 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 1);
var content = contentService.GetById(NodeDto.NodeIdSeed + 2);
// Act
bool published = contentService.PublishWithChildren(content, 0);
var children = contentService.GetChildren(NodeDto.NodeIdSeed + 1);
var children = contentService.GetChildren(NodeDto.NodeIdSeed + 2);
// Assert
Assert.That(published, Is.True);//Nothing was cancelled, so should be true
Assert.That(content.Published, Is.True);//No restrictions, so should be published
Assert.That(children.First(x => x.Id == NodeDto.NodeIdSeed + 2).Published, Is.True);//Released 5 mins ago, so should be published
Assert.That(children.First(x => x.Id == NodeDto.NodeIdSeed + 3).Published, Is.True);//Released 5 mins ago, so should be published
}
[Test]
@@ -1248,11 +1272,11 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 3); //This Content expired 5min ago
var content = contentService.GetById(NodeDto.NodeIdSeed + 4); //This Content expired 5min ago
content.ExpireDate = DateTime.Now.AddMinutes(-5);
contentService.Save(content);
var parent = contentService.GetById(NodeDto.NodeIdSeed + 1);
var parent = contentService.GetById(NodeDto.NodeIdSeed + 2);
bool parentPublished = contentService.Publish(parent, 0);//Publish root Home node to enable publishing of 'NodeDto.NodeIdSeed + 3'
// Act
@@ -1269,11 +1293,11 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 2);
var content = contentService.GetById(NodeDto.NodeIdSeed + 3);
content.ReleaseDate = DateTime.Now.AddHours(2);
contentService.Save(content, 0);
var parent = contentService.GetById(NodeDto.NodeIdSeed + 1);
var parent = contentService.GetById(NodeDto.NodeIdSeed + 2);
bool parentPublished = contentService.Publish(parent, 0);//Publish root Home node to enable publishing of 'NodeDto.NodeIdSeed + 3'
// Act
@@ -1290,7 +1314,7 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.CreateContent("Subpage with Unpublisehed Parent", NodeDto.NodeIdSeed + 1, "umbTextpage", 0);
var content = contentService.CreateContent("Subpage with Unpublisehed Parent", NodeDto.NodeIdSeed + 2, "umbTextpage", 0);
contentService.Save(content, 0);
// Act
@@ -1306,7 +1330,7 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
var content = contentService.GetById(NodeDto.NodeIdSeed + 5);
// Act
bool published = contentService.Publish(content, 0);
@@ -1373,17 +1397,17 @@ namespace Umbraco.Tests.Services
// Arrange
var contentService = ServiceContext.ContentService;
var root = contentService.GetById(NodeDto.NodeIdSeed + 1);
var root = contentService.GetById(NodeDto.NodeIdSeed + 2);
var rootPublished = contentService.Publish(root);
var content = contentService.GetById(NodeDto.NodeIdSeed + 3);
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
content.Properties["title"].SetValue(content.Properties["title"].GetValue() + " Published");
var contentPublished = contentService.SaveAndPublish(content);
var publishedVersion = content.Version;
content.Properties["title"].SetValue(content.Properties["title"].GetValue() + " Saved");
contentService.Save(content);
var savedVersion = content.Version;
Assert.AreEqual(publishedVersion, content.Version);
// Act
var publishedDescendants = ((ContentService) contentService).GetPublishedDescendants(root).ToList();
@@ -1396,23 +1420,21 @@ namespace Umbraco.Tests.Services
//Console.WriteLine(publishedVersion);
//foreach (var d in publishedDescendants) Console.WriteLine(d.Version);
Assert.IsTrue(publishedDescendants.Any(x => x.Version == publishedVersion));
Assert.IsFalse(publishedDescendants.Any(x => x.Version == savedVersion));
//Ensure that the published content version has the correct property value and is marked as published
var publishedContentVersion = publishedDescendants.First(x => x.Version == publishedVersion);
Assert.That(publishedContentVersion.Published, Is.True);
Assert.That(publishedContentVersion.Properties["title"].GetValue(), Contains.Substring("Published"));
Assert.That(publishedContentVersion.Properties["title"].GetValue(true), Contains.Substring("Published"));
//Ensure that the saved content version has the correct property value and is not marked as published
var savedContentVersion = contentService.GetByVersion(savedVersion);
Assert.That(savedContentVersion.Published, Is.False);
Assert.That(savedContentVersion.Properties["title"].GetValue(), Contains.Substring("Saved"));
// and has the correct draft properties
Assert.That(publishedContentVersion.Properties["title"].GetValue(), Contains.Substring("Saved"));
//Ensure that the latest version of the content is the saved and not-yet-published one
var currentContent = contentService.GetById(NodeDto.NodeIdSeed + 3);
Assert.That(currentContent.Published, Is.False);
//Ensure that the latest version of the content is ok
var currentContent = contentService.GetById(NodeDto.NodeIdSeed + 4);
Assert.That(currentContent.Published, Is.True);
Assert.That(currentContent.Properties["title"].GetValue(true), Contains.Substring("Published"));
Assert.That(currentContent.Properties["title"].GetValue(), Contains.Substring("Saved"));
Assert.That(currentContent.Version, Is.EqualTo(savedVersion));
Assert.That(currentContent.Version, Is.EqualTo(publishedContentVersion.Version));
}
[Test]
@@ -1520,10 +1542,10 @@ namespace Umbraco.Tests.Services
var contentService = ServiceContext.ContentService;
var contentType = ServiceContext.ContentTypeService.Get("umbTextpage");
var subsubpage = MockedContent.CreateSimpleContent(contentType, "Text Page 3", NodeDto.NodeIdSeed + 2);
var subsubpage = MockedContent.CreateSimpleContent(contentType, "Text Page 3", NodeDto.NodeIdSeed + 3);
contentService.Save(subsubpage, 0);
var content = contentService.GetById(NodeDto.NodeIdSeed + 1);
var content = contentService.GetById(NodeDto.NodeIdSeed + 2);
var descendants = contentService.GetDescendants(content).ToList();
Assert.AreNotEqual(-20, content.ParentId);
Assert.IsFalse(content.Trashed);
@@ -1571,6 +1593,7 @@ namespace Umbraco.Tests.Services
{
new ContentTypeSort(new Lazy<int>(() => contentType.Id), 0, contentType.Alias)
};
ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate);
ServiceContext.ContentTypeService.Save(contentType);
var parentPage = MockedContent.CreateSimpleContent(contentType);
@@ -1606,6 +1629,7 @@ namespace Umbraco.Tests.Services
{
new ContentTypeSort(new Lazy<int>(() => contentType.Id), 0, contentType.Alias)
};
ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate);
ServiceContext.ContentTypeService.Save(contentType);
var parentPage = MockedContent.CreateSimpleContent(contentType);
@@ -1730,13 +1754,13 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
var content = contentService.GetById(NodeDto.NodeIdSeed + 5);
// Act - moving out of recycle bin
contentService.Move(content, NodeDto.NodeIdSeed + 1, 0);
contentService.Move(content, NodeDto.NodeIdSeed + 2);
// Assert
Assert.That(content.ParentId, Is.EqualTo(NodeDto.NodeIdSeed + 1));
Assert.That(content.ParentId, Is.EqualTo(NodeDto.NodeIdSeed + 2));
Assert.That(content.Trashed, Is.False);
Assert.That(content.Published, Is.False);
}
@@ -1746,11 +1770,11 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var temp = contentService.GetById(NodeDto.NodeIdSeed + 3);
var temp = contentService.GetById(NodeDto.NodeIdSeed + 4);
// Act
var copy = contentService.Copy(temp, temp.ParentId, false, 0);
var content = contentService.GetById(NodeDto.NodeIdSeed + 3);
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
// Assert
Assert.That(copy, Is.Not.Null);
@@ -1758,7 +1782,6 @@ namespace Umbraco.Tests.Services
Assert.AreNotSame(content, copy);
foreach (var property in copy.Properties)
{
Assert.AreNotEqual(property.Id, content.Properties[property.Alias].Id);
Assert.AreEqual(property.GetValue(), content.Properties[property.Alias].GetValue());
}
//Assert.AreNotEqual(content.Name, copy.Name);
@@ -1769,13 +1792,13 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var temp = contentService.GetById(NodeDto.NodeIdSeed + 1);
var temp = contentService.GetById(NodeDto.NodeIdSeed + 2);
Assert.AreEqual("Home", temp.Name);
Assert.AreEqual(2, temp.Children(contentService).Count());
// Act
var copy = contentService.Copy(temp, temp.ParentId, false, true, 0);
var content = contentService.GetById(NodeDto.NodeIdSeed + 1);
var content = contentService.GetById(NodeDto.NodeIdSeed + 2);
// Assert
Assert.That(copy, Is.Not.Null);
@@ -1783,7 +1806,7 @@ namespace Umbraco.Tests.Services
Assert.AreNotSame(content, copy);
Assert.AreEqual(2, copy.Children(contentService).Count());
var child = contentService.GetById(NodeDto.NodeIdSeed + 2);
var child = contentService.GetById(NodeDto.NodeIdSeed + 3);
var childCopy = copy.Children(contentService).First();
Assert.AreEqual(childCopy.Name, child.Name);
Assert.AreNotEqual(childCopy.Id, child.Id);
@@ -1795,13 +1818,13 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var temp = contentService.GetById(NodeDto.NodeIdSeed + 1);
var temp = contentService.GetById(NodeDto.NodeIdSeed + 2);
Assert.AreEqual("Home", temp.Name);
Assert.AreEqual(2, temp.Children(contentService).Count());
// Act
var copy = contentService.Copy(temp, temp.ParentId, false, false, 0);
var content = contentService.GetById(NodeDto.NodeIdSeed + 1);
var content = contentService.GetById(NodeDto.NodeIdSeed + 2);
// Assert
Assert.That(copy, Is.Not.Null);
@@ -1833,47 +1856,94 @@ namespace Umbraco.Tests.Services
Assert.AreEqual("world", copiedTags[1].Text);
}
[Test, NUnit.Framework.Ignore("fixme - ignored test")]
public void Can_Send_To_Publication()
{ }
[Test]
public void Can_Rollback_Version_On_Content()
{
// Arrange
var contentService = ServiceContext.ContentService;
var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1);
var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 2);
Assert.IsFalse(parent.Published);
ServiceContext.ContentService.Publish(parent); // publishing parent, so Text Page 2 can be updated.
var subpage2 = contentService.GetById(NodeDto.NodeIdSeed + 3);
Assert.IsFalse(subpage2.Published);
var versions = contentService.GetVersions(NodeDto.NodeIdSeed + 3).ToList();
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
Assert.IsFalse(content.Published);
var versions = contentService.GetVersions(NodeDto.NodeIdSeed + 4).ToList();
Assert.AreEqual(1, versions.Count);
// new versions are only added when publishing changes,
// so change values and republish to ensure a version is created
// however because we're not published yet, the first publish will
// NOT create a new version, just publish the current one, so have
// to do it twice!
subpage2.Name = "Text Page 2 Updated";
subpage2.SetValue("author", "Francis Doe");
contentService.SaveAndPublishWithStatus(subpage2, 0); // publishes the current version
content.Name = "Text Page 2 Updated";
content.SetValue("author", "Francis Doe");
var version = subpage2.Version;
var nameBeforeRollback = subpage2.Name;
// non published = edited
Assert.IsTrue(content.Edited);
subpage2.Name = "Text Page 2 Updated1";
subpage2.SetValue("author", "Jane Doe");
contentService.SaveAndPublishWithStatus(subpage2, 0); // now creates a new version
contentService.SaveAndPublishWithStatus(content); // current version becomes 'published'
var rollback = contentService.Rollback(NodeDto.NodeIdSeed + 3, version, 0);
Assert.IsTrue(content.Published);
Assert.IsFalse(content.Edited);
Assert.AreEqual("Francis Doe", contentService.GetById(content.Id).GetValue<string>("author")); // version1 author is Francis
Assert.That(rollback, Is.Not.Null);
Assert.AreNotEqual(rollback.Version, subpage2.Version);
Assert.AreEqual("Text Page 2 Updated", content.Name);
Assert.AreEqual("Text Page 2 Updated", content.PublishName);
var version1 = content.Version;
content.Name = "Text Page 2 ReUpdated";
content.SetValue("author", "Jane Doe");
// is not actually 'edited' until changes have been saved
Assert.IsFalse(content.Edited);
contentService.Save(content); // just save changes
Assert.IsTrue(content.Edited);
Assert.AreEqual("Text Page 2 ReUpdated", content.Name);
Assert.AreEqual("Text Page 2 Updated", content.PublishName);
content.Name = "Text Page 2 ReReUpdated";
contentService.SaveAndPublishWithStatus(content); // saves a version
Assert.IsTrue(content.Published);
Assert.IsFalse(content.Edited);
Assert.AreEqual("Jane Doe", contentService.GetById(content.Id).GetValue<string>("author")); // version2 author is Jane
Assert.AreEqual("Text Page 2 ReReUpdated", content.Name);
Assert.AreEqual("Text Page 2 ReReUpdated", content.PublishName);
var version2 = content.Version;
Assert.AreNotEqual(version1, version2);
// rollback all values to version1
var rollback = contentService.Rollback(NodeDto.NodeIdSeed + 4, version1);
Assert.IsNotNull(rollback);
Assert.IsTrue(rollback.Published);
Assert.IsTrue(rollback.Edited);
Assert.AreEqual("Francis Doe", contentService.GetById(content.Id).GetValue<string>("author")); // version2 author is now Francis again
Assert.AreEqual(version2, rollback.Version); // same version but with edits
Assert.AreEqual("Francis Doe", rollback.GetValue<string>("author"));
Assert.AreEqual(nameBeforeRollback, rollback.Name);
Assert.AreEqual("Text Page 2 Updated", rollback.Name); // and the name has rolled back too
// can rollback to current too (clears changes)
var rollback2 = contentService.Rollback(NodeDto.NodeIdSeed + 4, version2);
Assert.IsTrue(rollback2.Published); // because current was published
Assert.IsFalse(rollback2.Edited); // all changes cleared!
// fixme - also test name here
content = contentService.GetById(content.Id);
Assert.AreEqual("Jane Doe", content.GetValue<string>("author")); // Francis was not published, so back to Jane
contentService.SaveAndPublishWithStatus(content);
Assert.IsFalse(content.Edited);
content.SetValue("author", "Bob Doe"); // changed to Bob
contentService.Save(content);
Assert.IsTrue(content.Edited);
content = contentService.Rollback(content.Id, content.Version);
Assert.IsFalse(content.Edited);
Assert.AreEqual("Jane Doe", content.GetValue("author")); // back to Jane
}
[Test]
@@ -1881,7 +1951,7 @@ namespace Umbraco.Tests.Services
{
var provider = TestObjects.GetScopeUnitOfWorkProvider(Mock.Of<ILogger>());
var contentType = ServiceContext.ContentTypeService.Get("umbTextpage");
var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1);
var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 2);
var c = new Lazy<IContent>(() => MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page", root.Id));
var c2 = new Lazy<IContent>(() => MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage", c.Value.Id));
@@ -1910,26 +1980,6 @@ namespace Umbraco.Tests.Services
}
[Test]
public void Can_Verify_Content_Has_Published_Version()
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 1);
bool published = contentService.PublishWithChildren(content, 0);
var homepage = contentService.GetById(NodeDto.NodeIdSeed + 1);
homepage.Name = "Homepage";
ServiceContext.ContentService.Save(homepage);
// Act
bool hasPublishedVersion = ServiceContext.ContentService.HasPublishedVersion(NodeDto.NodeIdSeed + 1);
// Assert
Assert.That(published, Is.True);
Assert.That(homepage.Published, Is.False);
Assert.That(hasPublishedVersion, Is.True);
}
[Test]
public void Can_Verify_Property_Types_On_Content()
{
@@ -1976,12 +2026,12 @@ namespace Umbraco.Tests.Services
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
var content = contentService.GetById(NodeDto.NodeIdSeed + 5);
var version = content.Version;
// Act
contentService.DeleteVersion(NodeDto.NodeIdSeed + 4, version, true, 0);
var sut = contentService.GetById(NodeDto.NodeIdSeed + 4);
contentService.DeleteVersion(NodeDto.NodeIdSeed + 5, version, true, 0);
var sut = contentService.GetById(NodeDto.NodeIdSeed + 5);
// Assert
Assert.That(sut.Version, Is.EqualTo(version));
@@ -2024,100 +2074,6 @@ namespace Umbraco.Tests.Services
}
}
[Test]
public void Created_HasPublishedVersion_Not()
{
var contentService = ServiceContext.ContentService;
var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0);
content.SetValue("author", "Barack Obama");
contentService.Save(content);
Assert.IsTrue(content.HasIdentity);
Assert.IsFalse(content.HasPublishedVersion);
content = contentService.GetById(content.Id);
Assert.IsFalse(content.HasPublishedVersion);
}
[Test]
public void Published_HasPublishedVersion_Self()
{
var contentService = ServiceContext.ContentService;
var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0);
content.SetValue("author", "Barack Obama");
contentService.SaveAndPublishWithStatus(content);
Assert.IsTrue(content.HasIdentity);
Assert.IsTrue(content.HasPublishedVersion);
Assert.AreEqual(content.PublishedVersionGuid, content.Version);
content = contentService.GetById(content.Id);
Assert.IsTrue(content.HasPublishedVersion);
Assert.AreEqual(content.PublishedVersionGuid, content.Version);
}
[Test]
public void PublishedWithChanges_HasPublishedVersion_Other()
{
var contentService = ServiceContext.ContentService;
var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0);
content.SetValue("author", "Barack Obama");
contentService.SaveAndPublishWithStatus(content);
content.SetValue("author", "James Dean");
contentService.Save(content);
Assert.IsTrue(content.HasIdentity);
Assert.IsTrue(content.HasPublishedVersion);
Assert.AreNotEqual(content.PublishedVersionGuid, content.Version);
content = contentService.GetById(content.Id);
Assert.IsFalse(content.Published);
Assert.IsTrue(content.HasPublishedVersion);
Assert.AreNotEqual(content.PublishedVersionGuid, content.Version);
var published = contentService.GetPublishedVersion(content);
Assert.IsTrue(published.Published);
Assert.IsTrue(published.HasPublishedVersion);
Assert.AreEqual(published.PublishedVersionGuid, published.Version);
}
[Test]
public void Unpublished_HasPublishedVersion_Not()
{
var contentService = ServiceContext.ContentService;
var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0);
content.SetValue("author", "Barack Obama");
contentService.SaveAndPublishWithStatus(content);
contentService.UnPublish(content);
Assert.IsTrue(content.HasIdentity);
Assert.IsFalse(content.HasPublishedVersion);
content = contentService.GetById(content.Id);
Assert.IsFalse(content.HasPublishedVersion);
}
[Test]
public void HasPublishedVersion_Method()
{
var contentService = ServiceContext.ContentService;
var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0);
content.SetValue("author", "Barack Obama");
contentService.Save(content);
Assert.IsTrue(content.HasIdentity);
Assert.IsFalse(content.HasPublishedVersion);
Assert.IsFalse(content.HasPublishedVersion());
contentService.SaveAndPublishWithStatus(content);
Assert.IsTrue(content.HasPublishedVersion);
Assert.IsTrue(content.HasPublishedVersion());
}
[Test]
public void Can_Get_Paged_Children()
{
@@ -2189,7 +2145,7 @@ namespace Umbraco.Tests.Services
private IEnumerable<IContent> CreateContentHierarchy()
{
var contentType = ServiceContext.ContentTypeService.Get("umbTextpage");
var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1);
var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 2);
var list = new List<IContent>();
@@ -19,7 +19,7 @@ namespace Umbraco.Tests.Services
{
Action<string, IContentType> addPropType = (alias, ct) =>
{
var contentCollection = new PropertyTypeCollection
var contentCollection = new PropertyTypeCollection(true)
{
new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) {Alias = alias, Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88}
};
@@ -59,7 +59,7 @@ namespace Umbraco.Tests.Services
{
Action<IContentType> addPropType = ct =>
{
var contentCollection = new PropertyTypeCollection
var contentCollection = new PropertyTypeCollection(true)
{
new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) {Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88}
};
@@ -96,7 +96,7 @@ namespace Umbraco.Tests.Services
{
Action<IContentType> addPropType = ct =>
{
var contentCollection = new PropertyTypeCollection
var contentCollection = new PropertyTypeCollection(true)
{
new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) {Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88}
};
@@ -279,20 +279,45 @@ namespace Umbraco.Tests.Services
{
var contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "Test1");
ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate);
ServiceContext.ContentTypeService.Save(contentType1);
var contentType2 = MockedContentTypes.CreateTextpageContentType("test2", "Test2");
ServiceContext.FileService.SaveTemplate(contentType2.DefaultTemplate);
ServiceContext.ContentTypeService.Save(contentType1);
ServiceContext.ContentTypeService.Save(contentType2);
var contentItems1 = MockedContent.CreateTextpageContent(contentType1, -1, 10).ToArray();
foreach (var x in contentItems1)
ServiceContext.ContentService.SaveAndPublishWithStatus(x);
var contentItems2 = MockedContent.CreateTextpageContent(contentType2, -1, 5).ToArray();
foreach (var x in contentItems2)
ServiceContext.ContentService.SaveAndPublishWithStatus(x);
//only update the contentType1 alias which will force an xml rebuild for all content of that type
// make sure we have everything
using (var scope = ScopeProvider.CreateScope())
{
foreach (var c in contentItems1)
{
var xml = scope.Database.FirstOrDefault<ContentXmlDto>("WHERE nodeId = @Id", new { Id = c.Id });
Assert.IsNotNull(xml);
Assert.IsTrue(xml.Xml.StartsWith("<test1"));
}
foreach (var c in contentItems2)
{
var xml = scope.Database.FirstOrDefault<ContentXmlDto>("WHERE nodeId = @Id", new { Id = c.Id });
Assert.IsNotNull(xml);
Assert.IsTrue(xml.Xml.StartsWith("<test2"));
}
scope.Complete();
}
// only update the contentType1 alias which will force an xml rebuild for all content of that type
contentType1.Alias = "newAlias";
ServiceContext.ContentTypeService.Save(contentType1);
// make sure updates have taken place
using (var scope = ScopeProvider.CreateScope())
{
foreach (var c in contentItems1)
@@ -1203,7 +1228,7 @@ namespace Umbraco.Tests.Services
//Change the name of the tab on the "root" content type 'page'.
var propertyGroup = contentPage.PropertyGroups["Content_"];
Assert.Throws<Exception>(() => contentPage.PropertyGroups.Add(new PropertyGroup
Assert.Throws<Exception>(() => contentPage.PropertyGroups.Add(new PropertyGroup(true)
{
Id = propertyGroup.Id,
Name = "Content",
@@ -1353,7 +1378,7 @@ namespace Umbraco.Tests.Services
Assert.That(page.PropertyGroups.Contains("Content_"), Is.True);
var propertyGroup = page.PropertyGroups["Content_"];
page.PropertyGroups.Add(new PropertyGroup{ Id = propertyGroup.Id, Name = "ContentTab", SortOrder = 0});
page.PropertyGroups.Add(new PropertyGroup(true) { Id = propertyGroup.Id, Name = "ContentTab", SortOrder = 0});
service.Save(page);
// Assert
@@ -1423,7 +1448,7 @@ namespace Umbraco.Tests.Services
//Change the name of the tab on the "root" content type 'page'.
var propertyGroup = page.PropertyGroups["Content_"];
page.PropertyGroups.Add(new PropertyGroup { Id = propertyGroup.Id, Name = "Content", SortOrder = 0 });
page.PropertyGroups.Add(new PropertyGroup(true) { Id = propertyGroup.Id, Name = "Content", SortOrder = 0 });
service.Save(page);
// Assert
@@ -1484,7 +1509,7 @@ namespace Umbraco.Tests.Services
//Change the name of the tab on the "root" content type 'page'.
var propertyGroup = page.PropertyGroups["Content_"];
page.PropertyGroups.Add(new PropertyGroup { Id = propertyGroup.Id, Name = "Content", SortOrder = 0 });
page.PropertyGroups.Add(new PropertyGroup(true) { Id = propertyGroup.Id, Name = "Content", SortOrder = 0 });
service.Save(page);
// Assert
@@ -1711,7 +1736,7 @@ namespace Umbraco.Tests.Services
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(true);
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "componentGroup") { Name = "Component Group", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
component.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Component", SortOrder = 1 });
@@ -1759,7 +1784,7 @@ namespace Umbraco.Tests.Services
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(true);
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "hostname") { Name = "Hostname", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
site.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Site Settings", SortOrder = 1 });
@@ -1781,7 +1806,7 @@ namespace Umbraco.Tests.Services
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(true);
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "title") { Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "bodyText") { Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "author") { Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 });
@@ -54,7 +54,7 @@ namespace Umbraco.Tests.Services
Assert.That(deletedDefinition, Is.Null);
//Further assertions against the ContentType that contains PropertyTypes based on the TextField
var contentType = ServiceContext.ContentTypeService.Get(NodeDto.NodeIdSeed);
var contentType = ServiceContext.ContentTypeService.Get(NodeDto.NodeIdSeed+1);
Assert.That(contentType.Alias, Is.EqualTo("umbTextpage"));
Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(1));
}
@@ -526,7 +526,7 @@ namespace Umbraco.Tests.Services
public void EntityService_Can_Get_Key_For_Id()
{
var service = ServiceContext.EntityService;
var result = service.GetKeyForId(1060, UmbracoObjectTypes.DocumentType);
var result = service.GetKeyForId(1061, UmbracoObjectTypes.DocumentType);
Assert.IsTrue(result.Success);
Assert.AreEqual(Guid.Parse("1D3A8E6E-2EA9-4CC1-B229-1AEE19821522"), result.Result);
@@ -536,8 +536,8 @@ namespace Umbraco.Tests.Services
public void EntityService_Cannot_Get_Key_For_Id_With_Incorrect_Object_Type()
{
var service = ServiceContext.EntityService;
var result1 = service.GetKeyForId(1060, UmbracoObjectTypes.DocumentType);
var result2 = service.GetKeyForId(1060, UmbracoObjectTypes.MediaType);
var result1 = service.GetKeyForId(1061, UmbracoObjectTypes.DocumentType);
var result2 = service.GetKeyForId(1061, UmbracoObjectTypes.MediaType);
Assert.IsTrue(result1.Success);
Assert.IsFalse(result2.Success);
@@ -550,7 +550,7 @@ namespace Umbraco.Tests.Services
var result = service.GetIdForKey(Guid.Parse("1D3A8E6E-2EA9-4CC1-B229-1AEE19821522"), UmbracoObjectTypes.DocumentType);
Assert.IsTrue(result.Success);
Assert.AreEqual(1060, result.Result);
Assert.AreEqual(1061, result.Result);
}
[Test]
@@ -421,6 +421,9 @@ namespace Umbraco.Tests.Services.Importing
var packagingService = ServiceContext.PackagingService;
var fileService = ServiceContext.FileService;
// kill default test data
fileService.DeleteTemplate("Textpage");
// Act
var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count();
var templates = packagingService.ImportTemplates(templateElement);
@@ -458,14 +458,15 @@ namespace Umbraco.Tests.Services
//NOTE: This will not trigger a property isDirty because this is not based on a 'Property', it is
// just a c# property of the Member object
resolved.Email = "changed@test.com";
//NOTE: this WILL trigger a property isDirty because setting this c# property actually sets a value of
// the underlying 'Property'
resolved.FailedPasswordAttempts = 1234;
var dirtyMember = (ICanBeDirty)resolved;
var dirtyMember = (ICanBeDirty) resolved;
var dirtyProperties = resolved.Properties.Where(x => x.IsDirty()).ToList();
Assert.IsTrue(dirtyMember.IsDirty());
Assert.AreEqual(1, dirtyProperties.Count());
Assert.AreEqual(1, dirtyProperties.Count);
}
[Test]
@@ -20,6 +20,7 @@ namespace Umbraco.Tests.Services
var contentService = ServiceContext.ContentService;
var contentTypeService = ServiceContext.ContentTypeService;
var ct = MockedContentTypes.CreateSimpleContentType("blah", "Blah");
ServiceContext.FileService.SaveTemplate(ct.DefaultTemplate);
contentTypeService.Save(ct);
var c = MockedContent.CreateSimpleContent(ct, "Test", -1);
contentService.Save(c);
@@ -54,6 +55,7 @@ namespace Umbraco.Tests.Services
var contentService = ServiceContext.ContentService;
var contentTypeService = ServiceContext.ContentTypeService;
var ct = MockedContentTypes.CreateSimpleContentType("blah", "Blah");
ServiceContext.FileService.SaveTemplate(ct.DefaultTemplate);
contentTypeService.Save(ct);
var c = MockedContent.CreateSimpleContent(ct, "Test", -1);
contentService.Save(c);
@@ -86,6 +88,7 @@ namespace Umbraco.Tests.Services
var contentService = ServiceContext.ContentService;
var contentTypeService = ServiceContext.ContentTypeService;
var ct = MockedContentTypes.CreateSimpleContentType("blah", "Blah");
ServiceContext.FileService.SaveTemplate(ct.DefaultTemplate);
contentTypeService.Save(ct);
var c = MockedContent.CreateSimpleContent(ct, "Test", -1);
contentService.Save(c);
@@ -122,6 +125,7 @@ namespace Umbraco.Tests.Services
var contentService = ServiceContext.ContentService;
var contentTypeService = ServiceContext.ContentTypeService;
var ct = MockedContentTypes.CreateSimpleContentType("blah", "Blah");
ServiceContext.FileService.SaveTemplate(ct.DefaultTemplate);
contentTypeService.Save(ct);
var c = MockedContent.CreateSimpleContent(ct, "Test", -1);
contentService.Save(c);
@@ -40,11 +40,11 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(true);
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 });
var metaCollection = new PropertyTypeCollection();
var metaCollection = new PropertyTypeCollection(true);
metaCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "keywords", Name = "Meta Keywords", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
metaCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "description", Name = "Meta Description", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -89 });
@@ -73,7 +73,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var metaCollection = new PropertyTypeCollection();
var metaCollection = new PropertyTypeCollection(true);
metaCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "metakeywords", Name = "Meta Keywords", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
metaCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "metadescription", Name = "Meta Description", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -89 });
@@ -99,7 +99,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var metaCollection = new PropertyTypeCollection();
var metaCollection = new PropertyTypeCollection(true);
metaCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentType.PropertyGroups.Add(new PropertyGroup(metaCollection) { Name = "Content", SortOrder = 2 });
@@ -124,7 +124,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var metaCollection = new PropertyTypeCollection();
var metaCollection = new PropertyTypeCollection(true);
metaCollection.Add(new PropertyType("seotest", DataTypeDatabaseType.Ntext) { Alias = "seokeywords", Name = "Seo Keywords", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
metaCollection.Add(new PropertyType("seotest", DataTypeDatabaseType.Ntext) { Alias = "seodescription", Name = "Seo Description", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -89 });
@@ -150,7 +150,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(true);
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 });
@@ -194,7 +194,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
contentType.CreatorId = 0;
contentType.Trashed = false;
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(true);
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("title", randomizeAliases), Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TinyMCEAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("bodyText", randomizeAliases), Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("author", randomizeAliases) , Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 });
@@ -223,7 +223,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
contentType.CreatorId = 0;
contentType.Trashed = false;
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(false);
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("title", randomizeAliases), Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TinyMCEAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("bodyText", randomizeAliases), Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("author", randomizeAliases), Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 });
@@ -251,7 +251,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(true);
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = mandatory, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", Mandatory = mandatory, SortOrder = 2, DataTypeDefinitionId = -87 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", Mandatory = mandatory, SortOrder = 3, DataTypeDefinitionId = -88 });
@@ -334,7 +334,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(true);
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer) { Alias = "isTrue", Name = "Is True or False", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -49 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer) { Alias = "number", Name = "Number", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -51 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TinyMCEAlias, DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -87 });
@@ -381,7 +381,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(false);
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Nvarchar) { Alias = "videoFile", Name = "Video File", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -90 });
@@ -407,7 +407,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(false);
contentCollection.Add(new PropertyType(Constants.PropertyEditors.UploadFieldAlias, DataTypeDatabaseType.Nvarchar) { Alias = Constants.Conventions.Media.File, Name = "File", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -90 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -90 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -90 });
@@ -436,7 +436,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
Trashed = false
};
var contentCollection = new PropertyTypeCollection();
var contentCollection = new PropertyTypeCollection(false);
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 });
+2 -2
View File
@@ -145,8 +145,8 @@ namespace Umbraco.Tests.TestHelpers
Assert.Fail($"{property.DeclaringType.Name}.{property.Name}: Expected {expectedPropertyValues.Length} but got {actualPropertyValues.Length}.");
for (var i = 0; i < expectedPropertyValues.Length; i++)
{
Assert.AreEqual(expectedPropertyValues[i].DraftValue, actualPropertyValues[i].DraftValue, $"{property.DeclaringType.Name}.{property.Name}: Expected draft value \"{expectedPropertyValues[i].DraftValue}\" but got \"{actualPropertyValues[i].DraftValue}\".");
Assert.AreEqual(expectedPropertyValues[i].PublishedValue, actualPropertyValues[i].PublishedValue, $"{property.DeclaringType.Name}.{property.Name}: Expected published value \"{expectedPropertyValues[i].DraftValue}\" but got \"{actualPropertyValues[i].DraftValue}\".");
Assert.AreEqual(expectedPropertyValues[i].EditValue, actualPropertyValues[i].EditValue, $"{property.DeclaringType.Name}.{property.Name}: Expected draft value \"{expectedPropertyValues[i].EditValue}\" but got \"{actualPropertyValues[i].EditValue}\".");
Assert.AreEqual(expectedPropertyValues[i].PublishedValue, actualPropertyValues[i].PublishedValue, $"{property.DeclaringType.Name}.{property.Name}: Expected published value \"{expectedPropertyValues[i].EditValue}\" but got \"{actualPropertyValues[i].EditValue}\".");
}
}
else
@@ -101,21 +101,21 @@ namespace Umbraco.Web.Editors
else
{
var valueEditor = property.PropertyEditor.ValueEditor;
//don't persist any bound value if the editor is readonly
if (valueEditor.IsReadOnly == false)
{
var propVal = property.PropertyEditor.ValueEditor.ConvertEditorToDb(data, dboProperty.GetValue());
var supportTagsAttribute = TagExtractor.GetAttribute(property.PropertyEditor);
if (supportTagsAttribute != null)
{
TagExtractor.SetPropertyTags(dboProperty, data, propVal, supportTagsAttribute);
}
else
{
dboProperty.SetValue(propVal);
}
}
// don't persist if the editor is readonly
if (valueEditor.IsReadOnly) continue;
// else
var propVal = property.PropertyEditor.ValueEditor.ConvertEditorToDb(data, dboProperty.GetValue());
var supportTagsAttribute = TagExtractor.GetAttribute(property.PropertyEditor);
if (supportTagsAttribute != null)
{
TagExtractor.SetPropertyTags(dboProperty, data, propVal, supportTagsAttribute);
}
else
{
dboProperty.SetValue(propVal);
}
}
}
}
+5 -3
View File
@@ -530,9 +530,11 @@ namespace Umbraco.Web.Editors
if (builtInAliases.Contains(p.Alias) == false && valueMapped != null)
{
p.SetValue(valueMapped.GetValue());
p.TagSupport.Behavior = valueMapped.TagSupport.Behavior;
p.TagSupport.Enable = valueMapped.TagSupport.Enable;
p.TagSupport.Tags = valueMapped.TagSupport.Tags;
// fixme - ok, I give up, at that point tags are dead here, until we figure it out
//p.TagChanges.Behavior = valueMapped.TagChanges.Behavior;
//p.TagChanges.Enable = valueMapped.TagChanges.Enable;
//p.TagChanges.Tags = valueMapped.TagChanges.Tags;
}
}
}
@@ -15,7 +15,6 @@ namespace Umbraco.Web.Models.ContentEditing
[DataContract(Name = "content", Namespace = "")]
public class ContentItemBasic : EntityBasic
{
[DataMember(Name = "updateDate")]
public DateTime UpdateDate { get; set; }
@@ -25,9 +24,6 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "published")]
public bool Published { get; set; }
[DataMember(Name = "hasPublishedVersion")]
public bool HasPublishedVersion { get; set; }
[DataMember(Name = "owner")]
public UserProfile Owner { get; set; }
@@ -16,7 +16,7 @@ namespace Umbraco.Web.Models.ContentEditing
/// Gets a value indicating whether this tab is the generic properties tab.
/// </summary>
[IgnoreDataMember]
public bool IsGenericProperties { get { return Id == GenericPropertiesGroupId; } }
public bool IsGenericProperties => Id == GenericPropertiesGroupId;
/// <summary>
/// Gets a value indicating whether the property group is inherited through
@@ -49,11 +49,7 @@ namespace Umbraco.Web.Models.ContentEditing
Properties = new List<TPropertyType>();
}
[DataMember(Name = "properties")]
public IEnumerable<TPropertyType> Properties { get; set; }
}
}
@@ -32,18 +32,18 @@ namespace Umbraco.Web.Models.Mapping
//FROM IContent TO ContentItemDisplay
CreateMap<IContent, ContentItemDisplay>()
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src =>
Udi.Create(src.IsBlueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, src.Key)))
Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, src.Key)))
.ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src)))
.ForMember(dest => dest.Updater, opt => opt.ResolveUsing(src => creatorResolver.Resolve(src)))
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon))
.ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias))
.ForMember(dest => dest.ContentTypeName, opt => opt.MapFrom(src => src.ContentType.Name))
.ForMember(dest => dest.IsContainer, opt => opt.MapFrom(src => src.ContentType.IsContainer))
.ForMember(dest => dest.IsBlueprint, opt => opt.MapFrom(src => src.Blueprint))
.ForMember(dest => dest.IsChildOfListView, opt => opt.Ignore())
.ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed))
.ForMember(dest => dest.PublishDate, opt => opt.MapFrom(src => GetPublishedDate(src)))
.ForMember(dest => dest.PublishDate, opt => opt.MapFrom(src => src.PublishDate))
.ForMember(dest => dest.TemplateAlias, opt => opt.MapFrom(src => src.Template.Alias))
.ForMember(dest => dest.HasPublishedVersion, opt => opt.MapFrom(src => src.HasPublishedVersion))
.ForMember(dest => dest.Urls, opt => opt.MapFrom(src =>
UmbracoContext.Current == null
? new[] {"Cannot generate urls without a current Umbraco Context"}
@@ -61,32 +61,24 @@ namespace Umbraco.Web.Models.Mapping
//FROM IContent TO ContentItemBasic<ContentPropertyBasic, IContent>
CreateMap<IContent, ContentItemBasic<ContentPropertyBasic, IContent>>()
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src =>
Udi.Create(src.IsBlueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, src.Key)))
Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, src.Key)))
.ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src)))
.ForMember(dest => dest.Updater, opt => opt.ResolveUsing(src => creatorResolver.Resolve(src)))
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon))
.ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed))
.ForMember(dest => dest.HasPublishedVersion, opt => opt.MapFrom(src => src.HasPublishedVersion))
.ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias))
.ForMember(dest => dest.Alias, opt => opt.Ignore());
//FROM IContent TO ContentItemDto<IContent>
CreateMap<IContent, ContentItemDto<IContent>>()
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src =>
Udi.Create(src.IsBlueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, src.Key)))
Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, src.Key)))
.ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src)))
.ForMember(dest => dest.HasPublishedVersion, opt => opt.MapFrom(src => src.HasPublishedVersion))
.ForMember(dest => dest.Updater, opt => opt.Ignore())
.ForMember(dest => dest.Icon, opt => opt.Ignore())
.ForMember(dest => dest.Alias, opt => opt.Ignore());
}
private static DateTime? GetPublishedDate(IContent content)
{
var date = ((Content) content).PublishedDate;
return date == default (DateTime) ? (DateTime?) null : date;
}
/// <summary>
/// Maps the generic tab with custom properties for content
/// </summary>
@@ -3,7 +3,6 @@ using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
using Umbraco.Web.Models.ContentEditing;
@@ -179,6 +179,8 @@ namespace Umbraco.Web.Models.Mapping
.IgnoreEntityCommonProperties()
.ForMember(dest => dest.IsPublishing, opt => opt.Ignore())
// see note above - have to do this here?
.ForMember(dest => dest.PropertyEditorAlias, opt => opt.Ignore())
.ForMember(dest => dest.DeletedDate, opt => opt.Ignore())
@@ -25,6 +25,7 @@ namespace Umbraco.Web.Models.Mapping
where TPropertyTypeBasic : PropertyTypeBasic
{
return mapping
.ConstructUsing(x => new PropertyGroup(false)) // fixme - we have NO idea of isPublishing here = wtf?
.IgnoreEntityCommonProperties()
.ForMember(dest => dest.Id, map => map.Condition(src => src.Id > 0))
.ForMember(dest => dest.Key, map => map.Ignore())
@@ -158,6 +159,9 @@ namespace Umbraco.Web.Models.Mapping
where TDestination : IContentTypeComposition
where TSourcePropertyType : PropertyTypeBasic
{
// fixme not so clean really
var isPublishing = typeof(IContentType).IsAssignableFrom(typeof(TDestination));
return mapping
//only map id if set to something higher then zero
.ForMember(dest => dest.Id, opt => opt.Condition(src => (Convert.ToInt32(src.Id) > 0)))
@@ -221,7 +225,7 @@ namespace Umbraco.Web.Models.Mapping
// ensure no duplicate alias, then assign the group properties collection
EnsureUniqueAliases(destProperties);
destGroup.PropertyTypes = new PropertyTypeCollection(destProperties);
destGroup.PropertyTypes = new PropertyTypeCollection(isPublishing, destProperties);
destGroups.Add(destGroup);
}
@@ -244,7 +248,7 @@ namespace Umbraco.Web.Models.Mapping
// ensure no duplicate alias, then assign the generic properties collection
EnsureUniqueAliases(destProperties);
dest.NoGroupPropertyTypes = new PropertyTypeCollection(destProperties);
dest.NoGroupPropertyTypes = new PropertyTypeCollection(isPublishing, destProperties);
}
// because all property collections were rebuilt, there is no need to remove
@@ -42,7 +42,6 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.Updater, opt => opt.Ignore())
.ForMember(dest => dest.Alias, opt => opt.Ignore())
.ForMember(dest => dest.IsContainer, opt => opt.Ignore())
.ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore())
.ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(src => tabsAndPropertiesResolver.Resolve(src)))
.AfterMap((src, dest) => AfterMap(src, dest, dataTypeService, textService, logger, mediaService));
@@ -55,8 +54,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias))
.ForMember(dest => dest.Published, opt => opt.Ignore())
.ForMember(dest => dest.Updater, opt => opt.Ignore())
.ForMember(dest => dest.Alias, opt => opt.Ignore())
.ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore());
.ForMember(dest => dest.Alias, opt => opt.Ignore());
//FROM IMedia TO ContentItemDto<IMedia>
CreateMap<IMedia, ContentItemDto<IMedia>>()
@@ -65,8 +63,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.Published, opt => opt.Ignore())
.ForMember(dest => dest.Updater, opt => opt.Ignore())
.ForMember(dest => dest.Icon, opt => opt.Ignore())
.ForMember(dest => dest.Alias, opt => opt.Ignore())
.ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore());
.ForMember(dest => dest.Alias, opt => opt.Ignore());
}
private static void AfterMap(IMedia media, MediaItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, ILogger logger, IMediaService mediaService)
@@ -61,6 +61,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
.ForMember(dest => dest.FailedPasswordAttempts, opt => opt.Ignore())
.ForMember(dest => dest.DeletedDate, opt => opt.Ignore())
.ForMember(dest => dest.WriterId, opt => opt.Ignore())
//TODO: Support these eventually
.ForMember(dest => dest.PasswordQuestion, opt => opt.Ignore())
.ForMember(dest => dest.RawPasswordAnswerValue, opt => opt.Ignore());
@@ -85,7 +86,6 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
.ForMember(dest => dest.IsContainer, opt => opt.Ignore())
.ForMember(dest => dest.TreeNodeUrl, opt => opt.Ignore())
.ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore())
.AfterMap((src, dest) => MapGenericCustomProperties(memberService, userService, src, dest, textService));
//FROM IMember TO MemberBasic
@@ -99,8 +99,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
.ForMember(dest => dest.Published, opt => opt.Ignore())
.ForMember(dest => dest.Updater, opt => opt.Ignore())
.ForMember(dest => dest.Alias, opt => opt.Ignore())
.ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore());
.ForMember(dest => dest.Alias, opt => opt.Ignore());
//FROM MembershipUser TO MemberBasic
CreateMap<MembershipUser, MemberBasic>()
@@ -124,8 +123,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.Updater, opt => opt.Ignore())
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
.ForMember(dest => dest.Alias, opt => opt.Ignore())
.ForMember(dest => dest.ContentTypeAlias, opt => opt.Ignore())
.ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore());
.ForMember(dest => dest.ContentTypeAlias, opt => opt.Ignore());
//FROM IMember TO ContentItemDto<IMember>
CreateMap<IMember, ContentItemDto<IMember>>()
@@ -135,7 +133,6 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.Updater, opt => opt.Ignore())
.ForMember(dest => dest.Icon, opt => opt.Ignore())
.ForMember(dest => dest.Alias, opt => opt.Ignore())
.ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore())
//do no map the custom member properties (currently anyways, they were never there in 6.x)
.ForMember(dest => dest.Properties, opt => opt.ResolveUsing(src => memberDtoPropertiesResolver.Resolve(src)));
+4 -3
View File
@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Models
@@ -27,6 +25,9 @@ namespace Umbraco.Web.Models
var propertyEditors = Current.PropertyEditors;
var dataTypeService = Current.Services.DataTypeService;
// fixme not dealing with variants
// but the entire thing should die anyways
return propertyTypes.Select(x =>
{
var p = properties.SingleOrDefault(xx => xx.Alias == x.PropertyTypeAlias);
@@ -47,7 +48,7 @@ namespace Umbraco.Web.Models
// nothing ensures that the two methods are consistent.
if (e != null)
v = e.ValueEditor.ConvertDbToString(p, p.PropertyType, dataTypeService);
v = e.ValueEditor.ConvertDbToString(p.PropertyType, v, dataTypeService);
}
return map(x, v);
@@ -160,13 +160,13 @@ namespace Umbraco.Web.PropertyEditors
}
public override string ConvertDbToString(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
public override string ConvertDbToString(PropertyType propertyType, object value, IDataTypeService dataTypeService)
{
if (property.GetValue() == null || string.IsNullOrEmpty(property.GetValue().ToString()))
if (value == null || string.IsNullOrEmpty(value.ToString()))
return null;
// if we dont have a json structure, we will get it from the property type
var val = property.GetValue().ToString();
var val = value.ToString();
if (val.DetectIsJson())
return val;
@@ -165,79 +165,59 @@ namespace Umbraco.Web.PropertyEditors
#region DB to String
public override string ConvertDbToString(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
public override string ConvertDbToString(PropertyType propertyType, object propertyValue, IDataTypeService dataTypeService)
{
// Convert / validate value
if (property.GetValue() == null || string.IsNullOrWhiteSpace(property.GetValue().ToString()))
if (propertyValue == null || string.IsNullOrWhiteSpace(propertyValue.ToString()))
return string.Empty;
var value = JsonConvert.DeserializeObject<List<object>>(property.GetValue().ToString());
var value = JsonConvert.DeserializeObject<List<object>>(propertyValue.ToString());
if (value == null)
return string.Empty;
// Process value
PreValueCollection preValues = null;
for (var i = 0; i < value.Count; i++)
foreach (var o in value)
{
var o = value[i];
var propValues = ((JObject)o);
var propValues = (JObject) o;
var contentType = GetElementType(propValues);
if (contentType == null)
{
continue;
}
var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();
foreach (var propKey in propValueKeys)
var propAliases = propValues.Properties().Select(x => x.Name).ToArray();
foreach (var propAlias in propAliases)
{
var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propKey);
var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propAlias);
if (propType == null)
{
if (IsSystemPropertyKey(propKey) == false)
{
// Property missing so just delete the value
propValues[propKey] = null;
}
// type not found, and property is not system: just delete the value
if (IsSystemPropertyKey(propAlias) == false)
propValues[propAlias] = null;
}
else
{
try
{
// Create a fake property using the property abd stored value
var prop = new Property(propType);
prop.SetValue(propValues[propKey] == null ? null : propValues[propKey].ToString());
// Lookup the property editor
// convert the value, and store the converted value
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
// Get the editor to do it's conversion, and store it back
propValues[propKey] = propEditor.ValueEditor.ConvertDbToString(prop, propType, dataTypeService);
var convValue = propEditor.ValueEditor.ConvertDbToString(propType, propValues[propAlias], dataTypeService);
propValues[propAlias] = convValue;
}
catch (InvalidOperationException)
{
// https://github.com/umco/umbraco-nested-content/issues/111
// Catch any invalid cast operations as likely means courier failed due to missing
// or trashed item so couldn't convert a guid back to an int
propValues[propKey] = null;
// deal with weird situations by ignoring them (no comment)
propValues[propAlias] = null;
}
}
}
}
// Update the value on the property
property.SetValue(JsonConvert.SerializeObject(value));
// Pass the call down
return base.ConvertDbToString(property, propertyType, dataTypeService);
return JsonConvert.SerializeObject(value).ToXmlString<string>();
}
#endregion
#region DB to Editor
#region Convert database // editor
// note: there is NO variant support here
public override object ConvertDbToEditor(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
{
@@ -248,73 +228,51 @@ namespace Umbraco.Web.PropertyEditors
if (value == null)
return string.Empty;
// Process value
PreValueCollection preValues = null;
for (var i = 0; i < value.Count; i++)
foreach (var o in value)
{
var o = value[i];
var propValues = ((JObject)o);
var propValues = (JObject) o;
var contentType = GetElementType(propValues);
if (contentType == null)
{
continue;
}
var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();
foreach (var propKey in propValueKeys)
var propAliases = propValues.Properties().Select(x => x.Name).ToArray();
foreach (var propAlias in propAliases)
{
var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propKey);
var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propAlias);
if (propType == null)
{
if (IsSystemPropertyKey(propKey) == false)
{
// Property missing so just delete the value
propValues[propKey] = null;
}
// type not found, and property is not system: just delete the value
if (IsSystemPropertyKey(propAlias) == false)
propValues[propAlias] = null;
}
else
{
try
{
// Create a fake property using the property and stored value
var prop = new Property(propType);
prop.SetValue(propValues[propKey] == null ? null : propValues[propKey].ToString());
// create a temp property with the value
var tempProp = new Property(propType);
tempProp.SetValue(propValues[propAlias] == null ? null : propValues[propAlias].ToString());
// Lookup the property editor
// convert that temp property, and store the converted value
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
// Get the editor to do it's conversion
var newValue = propEditor.ValueEditor.ConvertDbToEditor(prop, propType, dataTypeService);
// Store the value back
propValues[propKey] = (newValue == null) ? null : JToken.FromObject(newValue);
var convValue = propEditor.ValueEditor.ConvertDbToEditor(tempProp, propType, dataTypeService);
propValues[propAlias] = convValue == null ? null : JToken.FromObject(convValue);
}
catch (InvalidOperationException)
{
// https://github.com/umco/umbraco-nested-content/issues/111
// Catch any invalid cast operations as likely means courier failed due to missing
// or trashed item so couldn't convert a guid back to an int
propValues[propKey] = null;
// deal with weird situations by ignoring them (no comment)
propValues[propAlias] = null;
}
}
}
}
// Update the value on the property
property.SetValue(JsonConvert.SerializeObject(value));
// Pass the call down
return base.ConvertDbToEditor(property, propertyType, dataTypeService);
// return json
return value;
}
#endregion
#region Editor to DB
public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue)
{
if (editorValue.Value == null || string.IsNullOrWhiteSpace(editorValue.Value.ToString()))
@@ -40,16 +40,16 @@ namespace Umbraco.Web.PropertyEditors
/// <param name="propertyType"></param>
/// <param name="dataTypeService"></param>
/// <returns></returns>
public override string ConvertDbToString(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
public override string ConvertDbToString(PropertyType propertyType, object value, IDataTypeService dataTypeService)
{
if (property.GetValue() == null)
if (value == null)
return null;
var idAttempt = property.GetValue().TryConvertTo<int>();
var idAttempt = value.TryConvertTo<int>();
if (idAttempt.Success)
{
var preValId = idAttempt.Result;
var preVals = GetPreValues(property);
var preVals = GetPreValues(propertyType);
if (preVals != null)
{
if (preVals.Any(x => x.Value.Id == preValId))
@@ -57,16 +57,17 @@ namespace Umbraco.Web.PropertyEditors
return preVals.Single(x => x.Value.Id == preValId).Value.Value;
}
_logger.Warn<PublishValueValueEditor>("Could not find a pre value with ID " + preValId + " for property alias " + property.Alias);
_logger.Warn<PublishValueValueEditor>("Could not find a pre value with ID " + preValId + " for property alias " + propertyType.Alias);
}
}
return base.ConvertDbToString(property, propertyType, dataTypeService);
// fallback to default
return base.ConvertDbToString(propertyType, value, dataTypeService);
}
protected IDictionary<string, PreValue> GetPreValues(Property property)
protected IDictionary<string, PreValue> GetPreValues(PropertyType propertyType)
{
var preVals = _dataTypeService.GetPreValuesCollectionByDataTypeId(property.PropertyType.DataTypeDefinitionId);
var preVals = _dataTypeService.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeDefinitionId);
if (preVals != null)
{
var dictionary = preVals.FormatAsDictionary();
@@ -34,38 +34,38 @@ namespace Umbraco.Web.PropertyEditors
/// <summary>
/// If publishing ids, we don't need to do anything, otherwise we need to look up the pre-values and get the string values
/// </summary>
/// <param name="property"></param>
/// <param name="propertyType"></param>
/// <param name="propertyValue"></param>
/// <param name="dataTypeService"></param>
/// <returns></returns>
public override string ConvertDbToString(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
public override string ConvertDbToString(PropertyType propertyType, object propertyValue, IDataTypeService dataTypeService)
{
if (property.GetValue() == null)
if (propertyValue == null)
return null;
//publishing ids, so just need to return the value as-is
if (_publishIds)
{
return property.GetValue().ToString();
return propertyValue.ToString();
}
//get the multiple ids
var selectedIds = property.GetValue().ToString().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
var selectedIds = propertyValue.ToString().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
if (selectedIds.Any() == false)
{
//nothing there
return base.ConvertDbToString(property, propertyType, dataTypeService);
//nothing there, fallback to base
return base.ConvertDbToString(propertyType, propertyValue, dataTypeService);
}
var preValues = GetPreValues(property);
var preValues = GetPreValues(propertyType);
if (preValues != null)
{
//get all pre-values matching our Ids
return string.Join(",",
preValues.Where(x => selectedIds.Contains(x.Value.Id.ToInvariantString())).Select(x => x.Value.Value));
return string.Join(",", preValues.Where(x => selectedIds.Contains(x.Value.Id.ToInvariantString())).Select(x => x.Value.Value));
}
return base.ConvertDbToString(property, propertyType, dataTypeService);
// bah, fallback to base
return base.ConvertDbToString(propertyType, propertyValue, dataTypeService);
}
/// <summary>
@@ -14,21 +14,24 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
public int SortOrder { get; set; }
public int ParentId { get; set; }
public Guid Version { get; set; }
public DateTime CreateDate { get; set; }
public int CreatorId { get; set; }
public string DraftName { get; set; }
public Guid DraftVersion { get; set; }
public DateTime DraftVersionDate { get; set; }
public int DraftWriterId { get; set; }
public int DraftTemplateId { get; set; }
public string DraftData { get; set; }
public string PubName { get; set; }
public Guid PubVersion { get; set; }
public DateTime PubVersionDate { get; set; }
public int PubWriterId { get; set; }
public int PubTemplateId { get; set; }
public string PubData { get; set; }
public bool Published { get; set; }
public bool Edited { get; set; }
}
}
@@ -16,19 +16,32 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
{
public ContentNodeKit GetContentSource(IScopeUnitOfWork uow, int id)
{
// fixme - missing things
//
// new: documentDto.publishDate
// new: documentDto.publishUserId
// new: documentDto.templateId - the published one
// fixme still, we have an issue with names
//
// node.text == contentVersion.text (and we use contentVersion.text to keep track of things)
// !! when creating a new version, the old version versionDate = publishDate, what else?
//
// new: documentDto.publishName === THE ONE WE USE FOR URLS !!
//
var dto = uow.Database.Fetch<ContentSourceDto>(new Sql(@"SELECT
n.id Id, n.uniqueId Uid,
uContent.contentTypeId ContentTypeId,
n.level Level, n.path Path, n.sortOrder SortOrder, n.parentId ParentId,
n.level Level, n.path Path, n.sortOrder SortOrder, n.parentId ParentId, cver.versionId Version,
n.createDate CreateDate, n.nodeUser CreatorId,
docDraft.text DraftName, docDraft.versionId DraftVersion, docDraft.updateDate DraftVersionDate, docDraft.writerUserId DraftWriterId, docDraft.templateId DraftTemplateId,
cver.text DraftName, c.updateDate DraftVersionDate, c.writerUserId DraftWriterId, dver.templateId DraftTemplateId, doc.edited Edited,
nuDraft.data DraftData,
docPub.text PubName, docPub.versionId PubVersion, docPub.updateDate PubVersionDate, docPub.writerUserId PubWriterId, docPub.templateId PubTemplateId,
docPub.text PubName, doc.publishDate PubVersionDate, doc.publishUserId PubWriterId, doc.templateId PubTemplateId, doc.published Published,
nuPub.data PubData
FROM umbracoNode n
JOIN uContent ON (uContent.nodeId=n.id)
LEFT JOIN cmsDocument docDraft ON (docDraft.nodeId=n.id AND docDraft.newest=1 AND docDraft.published=0)
LEFT JOIN cmsDocument docPub ON (docPub.nodeId=n.id AND docPub.published=1)
JOIN uContent c ON (c.nodeId=n.id)
LEFT JOIN uDocument doc ON (doc.nodeId=n.id)
LEFT JOIN uContentVersion cver ON (n.id=cver.nodeId AND cver.current=1)
LEFT JOIN uDocumentVersion dver ON (cver.id=dver.id)
LEFT JOIN cmsContentNu nuDraft ON (nuDraft.nodeId=n.id AND nuDraft.published=0)
LEFT JOIN cmsContentNu nuPub ON (nuPub.nodeId=n.id AND nuPub.published=1)
WHERE n.nodeObjectType=@objType AND n.id=@id
@@ -49,7 +62,7 @@ n.text PubName, ver.versionId PubVersion, ver.versionDate PubVersionDate,
nuPub.data PubData
FROM umbracoNode n
JOIN uContent ON (uContent.nodeId=n.id)
JOIN cmsContentVersion ver ON (ver.contentId=n.id)
JOIN uContentVersion ver ON (ver.contentId=n.id)
LEFT JOIN cmsContentNu nuPub ON (nuPub.nodeId=n.id AND nuPub.published=1)
WHERE n.nodeObjectType=@objType AND n.id=@id
", new { objType = Constants.ObjectTypes.Media, /*id =*/ id })).FirstOrDefault();
@@ -93,7 +106,7 @@ n.text PubName, ver.versionId PubVersion, ver.versionDate PubVersionDate,
nuPub.data PubData
FROM umbracoNode n
JOIN uContent ON (uContent.nodeId=n.id)
JOIN cmsContentVersion ver ON (ver.contentId=n.id)
JOIN uContentVersion ver ON (ver.contentId=n.id)
LEFT JOIN cmsContentNu nuPub ON (nuPub.nodeId=n.id AND nuPub.published=1)
WHERE n.nodeObjectType=@objType
ORDER BY n.level, n.sortOrder
@@ -137,7 +150,7 @@ nuPub.data PubData
FROM umbracoNode n
JOIN umbracoNode x ON (n.id=x.id OR n.path LIKE " + uow.SqlContext.SqlSyntax.GetConcat("x.path", "',%'") + @")
JOIN uContent ON (uContent.nodeId=n.id)
JOIN cmsContentVersion ver ON (ver.contentId=n.id)
JOIN uContentVersion ver ON (ver.contentId=n.id)
LEFT JOIN cmsContentNu nuPub ON (nuPub.nodeId=n.id AND nuPub.published=1)
WHERE n.nodeObjectType=@objType AND x.id=@id
ORDER BY n.level, n.sortOrder
@@ -179,7 +192,7 @@ n.text PubName, ver.versionId PubVersion, ver.versionDate PubVersionDate,
nuPub.data PubData
FROM umbracoNode n
JOIN uContent ON (uContent.nodeId=n.id)
JOIN cmsContentVersion ver ON (ver.contentId=n.id)
JOIN uContentVersion ver ON (ver.contentId=n.id)
LEFT JOIN cmsContentNu nuPub ON (nuPub.nodeId=n.id AND nuPub.published=1)
WHERE n.nodeObjectType=@objType AND uContent.contentTypeId IN (@ids)
ORDER BY n.level, n.sortOrder
@@ -191,7 +204,7 @@ ORDER BY n.level, n.sortOrder
ContentData d = null;
ContentData p = null;
if (dto.DraftVersion != Guid.Empty)
if (dto.Edited)
{
if (dto.DraftData == null)
{
@@ -205,7 +218,7 @@ ORDER BY n.level, n.sortOrder
Name = dto.DraftName,
Published = false,
TemplateId = dto.DraftTemplateId,
Version = dto.DraftVersion,
Version = dto.Version,
VersionDate = dto.DraftVersionDate,
WriterId = dto.DraftWriterId,
Properties = DeserializeData(dto.DraftData)
@@ -213,7 +226,7 @@ ORDER BY n.level, n.sortOrder
}
}
if (dto.PubVersion != Guid.Empty)
if (dto.Published)
{
if (dto.PubData == null)
{
@@ -227,7 +240,7 @@ ORDER BY n.level, n.sortOrder
Name = dto.PubName,
Published = true,
TemplateId = dto.PubTemplateId,
Version = dto.PubVersion,
Version = dto.Version,
VersionDate = dto.PubVersionDate,
WriterId = dto.PubWriterId,
Properties = DeserializeData(dto.PubData)
@@ -259,7 +272,7 @@ ORDER BY n.level, n.sortOrder
Name = dto.PubName,
Published = true,
TemplateId = -1,
Version = dto.PubVersion,
Version = dto.Version,
VersionDate = dto.PubVersionDate,
WriterId = dto.CreatorId, // what-else?
Properties = DeserializeData(dto.PubData)

Some files were not shown because too many files have changed in this diff Show More