var alias = match.Groups[2].Value;
var sb = new StringBuilder("
5000 || image.Height > 5000)
+ {
+ //use mid quality
+ g.InterpolationMode = InterpolationMode.Bilinear;
+ }
+ else
+ {
+ //use best quality
+ g.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ }
+
+
+ g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs
index 3fbdeccda8..c18c7c3a43 100644
--- a/src/Umbraco.Core/Models/ContentBase.cs
+++ b/src/Umbraco.Core/Models/ContentBase.cs
@@ -306,10 +306,8 @@ namespace Umbraco.Core.Models
///
Value as a
public virtual TPassType GetValue
(string propertyTypeAlias)
{
- if (Properties[propertyTypeAlias].Value is TPassType)
- return (TPassType)Properties[propertyTypeAlias].Value;
-
- return (TPassType)Convert.ChangeType(Properties[propertyTypeAlias].Value, typeof(TPassType));
+ var convertAttempt = Properties[propertyTypeAlias].Value.TryConvertTo();
+ return convertAttempt.Success ? convertAttempt.Result : default(TPassType);
}
///
diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs
index d377095a05..73b5ed34db 100644
--- a/src/Umbraco.Core/Models/ContentExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentExtensions.cs
@@ -26,6 +26,20 @@ namespace Umbraco.Core.Models
{
#region IContent
+ ///
+ /// Returns true if this entity was just published as part of a recent save operation (i.e. it wasn't previously published)
+ ///
+ ///
+ ///
+ ///
+ /// This is helpful for determining if the published event will execute during the saved event for a content item.
+ ///
+ internal static bool JustPublished(this IContent entity)
+ {
+ var dirty = (IRememberBeingDirty)entity;
+ return dirty.WasPropertyDirty("Published") && entity.Published;
+ }
+
///
/// Determines if a new version should be created
///
@@ -618,7 +632,7 @@ namespace Umbraco.Core.Models
/// Xml representation of the passed in
internal static XElement ToDeepXml(this IContent content)
{
- return ApplicationContext.Current.Services.PackagingService.Export(content, true);
+ return ApplicationContext.Current.Services.PackagingService.Export(content, true, raiseEvents: false);
}
///
@@ -628,7 +642,7 @@ namespace Umbraco.Core.Models
/// Xml representation of the passed in
public static XElement ToXml(this IContent content)
{
- return ApplicationContext.Current.Services.PackagingService.Export(content);
+ return ApplicationContext.Current.Services.PackagingService.Export(content, raiseEvents: false);
}
///
@@ -638,24 +652,19 @@ namespace Umbraco.Core.Models
/// Xml representation of the passed in
public static XElement ToXml(this IMedia media)
{
- return ApplicationContext.Current.Services.PackagingService.Export(media);
- }
-
- internal static XElement ToDeepXml(this IMedia media)
- {
- return ApplicationContext.Current.Services.PackagingService.Export(media, true);
+ return ApplicationContext.Current.Services.PackagingService.Export(media, raiseEvents: false);
}
///
- /// Creates the xml representation for the object
+ /// Creates the full xml representation for the object and all of it's descendants
///
- /// to generate xml for
- /// Xml representation of the passed in
- public static XElement ToXml(this IMember member)
+ /// to generate xml for
+ /// Xml representation of the passed in
+ internal static XElement ToDeepXml(this IMedia media)
{
- return ApplicationContext.Current.Services.PackagingService.Export(member);
+ return ApplicationContext.Current.Services.PackagingService.Export(media, true, raiseEvents: false);
}
-
+
///
/// Creates the xml representation for the object
///
@@ -668,6 +677,16 @@ namespace Umbraco.Core.Models
//If current IContent is published we should get latest unpublished version
return content.ToXml();
}
+
+ ///
+ /// Creates the xml representation for the object
+ ///
+ /// to generate xml for
+ /// Xml representation of the passed in
+ public static XElement ToXml(this IMember member)
+ {
+ return ((PackagingService)(ApplicationContext.Current.Services.PackagingService)).Export(member);
+ }
#endregion
}
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index 87acf07d45..f22c72f34d 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -43,6 +43,7 @@ namespace Umbraco.Core.Models
_allowedContentTypes = new List();
_propertyGroups = new PropertyGroupCollection();
_propertyTypes = new PropertyTypeCollection();
+ _propertyTypes.CollectionChanged += PropertyTypesChanged;
_additionalData = new Dictionary();
}
@@ -54,6 +55,7 @@ namespace Umbraco.Core.Models
_allowedContentTypes = new List();
_propertyGroups = new PropertyGroupCollection();
_propertyTypes = new PropertyTypeCollection();
+ _propertyTypes.CollectionChanged += PropertyTypesChanged;
_additionalData = new Dictionary();
}
@@ -433,8 +435,7 @@ namespace Umbraco.Core.Models
{
if (PropertyTypeExists(propertyType.Alias) == false)
{
- _propertyTypes.Add(propertyType);
- _propertyTypes.CollectionChanged += PropertyTypesChanged;
+ _propertyTypes.Add(propertyType);
return true;
}
diff --git a/src/Umbraco.Core/Models/DictionaryItem.cs b/src/Umbraco.Core/Models/DictionaryItem.cs
index db3f47ba13..c670a75108 100644
--- a/src/Umbraco.Core/Models/DictionaryItem.cs
+++ b/src/Umbraco.Core/Models/DictionaryItem.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
-using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Models
{
diff --git a/src/Umbraco.Core/Models/EntityExtensions.cs b/src/Umbraco.Core/Models/EntityExtensions.cs
new file mode 100644
index 0000000000..6daf99a58d
--- /dev/null
+++ b/src/Umbraco.Core/Models/EntityExtensions.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Models
+{
+ public static class EntityExtensions
+ {
+
+ ///
+ /// Returns true if this entity has just been created and persisted to the data store
+ ///
+ ///
+ ///
+ ///
+ /// This is useful when handling events to determine if an entity is a brand new entity or was
+ /// already existing.
+ ///
+ public static bool IsNewEntity(this IEntity entity)
+ {
+ var dirty = (IRememberBeingDirty)entity;
+ return dirty.WasPropertyDirty("Id");
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs
index 978e54390f..f520b63210 100644
--- a/src/Umbraco.Core/Models/File.cs
+++ b/src/Umbraco.Core/Models/File.cs
@@ -6,6 +6,16 @@ using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
+ internal sealed class Folder : Entity
+ {
+ public Folder(string folderPath)
+ {
+ Path = folderPath;
+ }
+
+ public string Path { get; set; }
+ }
+
///
/// Represents an abstract file which provides basic functionality for a File with an Alias and Name
///
@@ -23,6 +33,8 @@ namespace Umbraco.Core.Models
private static readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo(x => x.Content);
private static readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo(x => x.Path);
+ private string _alias;
+ private string _name;
///
/// Gets or sets the Name of the File including extension
@@ -32,7 +44,11 @@ namespace Umbraco.Core.Models
{
get
{
- return new FileInfo(Path).Name;
+ if (_name == null)
+ {
+ _name = System.IO.Path.GetFileName(Path);
+ }
+ return _name;
}
}
@@ -44,10 +60,14 @@ namespace Umbraco.Core.Models
{
get
{
- var fileInfo = new FileInfo(Path);
- var name = fileInfo.Name;
- int lastIndexOf = name.LastIndexOf(".", StringComparison.InvariantCultureIgnoreCase);
- return name.Substring(0, lastIndexOf);
+ if (_alias == null)
+ {
+ var name = System.IO.Path.GetFileName(Path);
+ if (name == null) return string.Empty;
+ var lastIndexOf = name.LastIndexOf(".", StringComparison.InvariantCultureIgnoreCase);
+ _alias = name.Substring(0, lastIndexOf);
+ }
+ return _alias;
}
}
@@ -60,6 +80,10 @@ namespace Umbraco.Core.Models
get { return _path; }
set
{
+ //reset
+ _alias = null;
+ _name = null;
+
SetPropertyValueAndDetectChanges(o =>
{
_path = value;
diff --git a/src/Umbraco.Core/Models/IMember.cs b/src/Umbraco.Core/Models/IMember.cs
index edacd0b62b..d5838f30ff 100644
--- a/src/Umbraco.Core/Models/IMember.cs
+++ b/src/Umbraco.Core/Models/IMember.cs
@@ -1,106 +1,10 @@
using System;
+using Umbraco.Core.Models.Membership;
namespace Umbraco.Core.Models
{
- public interface IMember : IContentBase
+ public interface IMember : IContentBase, IMembershipUser
{
- ///
- /// Gets or sets the Username
- ///
- string Username { get; set; }
-
- ///
- /// Gets or sets the Email
- ///
- string Email { get; set; }
-
- ///
- /// Gets or sets the Password
- ///
- string Password { get; set; }
-
- ///
- /// Gets or sets the Password Question
- ///
- ///
- /// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias
- /// Part of the standard properties collection.
- ///
- string PasswordQuestion { get; set; }
-
- ///
- /// Gets or sets the Password Answer
- ///
- ///
- /// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias
- /// Part of the standard properties collection.
- ///
- string PasswordAnswer { get; set; }
-
- ///
- /// Gets or set the comments for the member
- ///
- ///
- /// Alias: umbracoCommentPropertyTypeAlias
- /// Part of the standard properties collection.
- ///
- string Comments { get; set; }
-
- ///
- /// Gets or sets a boolean indicating whether the Member is approved
- ///
- ///
- /// Alias: umbracoApprovePropertyTypeAlias
- /// Part of the standard properties collection.
- ///
- bool IsApproved { get; set; }
-
- ///
- /// Gets or sets a boolean indicating whether the Member is locked out
- ///
- ///
- /// Alias: umbracoLockPropertyTypeAlias
- /// Part of the standard properties collection.
- ///
- bool IsLockedOut { get; set; }
-
- ///
- /// Gets or sets the date for last login
- ///
- ///
- /// Alias: umbracoLastLoginPropertyTypeAlias
- /// Part of the standard properties collection.
- ///
- DateTime LastLoginDate { get; set; }
-
- ///
- /// Gest or sets the date for last password change
- ///
- ///
- /// Alias: umbracoMemberLastPasswordChange
- /// Part of the standard properties collection.
- ///
- DateTime LastPasswordChangeDate { get; set; }
-
- ///
- /// Gets or sets the date for when Member was locked out
- ///
- ///
- /// Alias: umbracoMemberLastLockout
- /// Part of the standard properties collection.
- ///
- DateTime LastLockoutDate { get; set; }
-
- ///
- /// Gets or sets the number of failed password attempts.
- /// This is the number of times the password was entered incorrectly upon login.
- ///
- ///
- /// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias
- /// Part of the standard properties collection.
- ///
- int FailedPasswordAttempts { get; set; }
-
///
/// String alias of the default ContentType
///
diff --git a/src/Umbraco.Core/Models/IMemberGroup.cs b/src/Umbraco.Core/Models/IMemberGroup.cs
new file mode 100644
index 0000000000..5c3741997b
--- /dev/null
+++ b/src/Umbraco.Core/Models/IMemberGroup.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Represents a member type
+ ///
+ public interface IMemberGroup : IAggregateRoot
+ {
+ ///
+ /// The name of the member group
+ ///
+ string Name { get; set; }
+
+ ///
+ /// Profile of the user who created this Entity
+ ///
+ int CreatorId { get; set; }
+
+ ///
+ /// Some entities may expose additional data that other's might not, this custom data will be available in this collection
+ ///
+ IDictionary AdditionalData { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/ITemplate.cs b/src/Umbraco.Core/Models/ITemplate.cs
index 2014ac9837..7fb4eac4cf 100644
--- a/src/Umbraco.Core/Models/ITemplate.cs
+++ b/src/Umbraco.Core/Models/ITemplate.cs
@@ -10,5 +10,11 @@
///
///
RenderingEngine GetTypeOfRenderingEngine();
+
+ ///
+ /// Set the mastertemplate
+ ///
+ ///
+ void SetMasterTemplate(ITemplate masterTemplate);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs
index 6986fae027..ef8ebbf931 100644
--- a/src/Umbraco.Core/Models/Language.cs
+++ b/src/Umbraco.Core/Models/Language.cs
@@ -3,7 +3,6 @@ using System.Globalization;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
-using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Models
{
diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs
index 914847aea2..d81f49fc4f 100644
--- a/src/Umbraco.Core/Models/Macro.cs
+++ b/src/Umbraco.Core/Models/Macro.cs
@@ -107,7 +107,7 @@ namespace Umbraco.Core.Models
private bool _dontRender;
private string _scriptFile;
private string _scriptAssembly;
- private string _python;
+ private string _scriptPath;
private string _xslt;
private readonly MacroPropertyCollection _properties;
private readonly List _addedProperties;
@@ -360,14 +360,14 @@ namespace Umbraco.Core.Models
[DataMember]
public string ScriptPath
{
- get { return _python; }
+ get { return _scriptPath; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
- _python = value;
- return _python;
- }, _python, ScriptPathSelector);
+ _scriptPath = value;
+ return _scriptPath;
+ }, _scriptPath, ScriptPathSelector);
}
}
diff --git a/src/Umbraco.Core/Models/MacroProperty.cs b/src/Umbraco.Core/Models/MacroProperty.cs
index 243e5c0e91..ef0a024973 100644
--- a/src/Umbraco.Core/Models/MacroProperty.cs
+++ b/src/Umbraco.Core/Models/MacroProperty.cs
@@ -155,7 +155,6 @@ namespace Umbraco.Core.Models
///
[DataMember]
public string EditorAlias
-
{
get { return _editorAlias; }
set
diff --git a/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs
index 4864c4bba4..f9c0c2cc1e 100644
--- a/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs
+++ b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Models.Mapping
/// All automapper configurations are done during startup
/// inside an Automapper Initialize call which is better for performance
///
- internal interface IMapperConfiguration : IApplicationEventHandler
+ public interface IMapperConfiguration : IApplicationEventHandler
{
void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext);
}
diff --git a/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs
index 1e88e648cf..52683231e2 100644
--- a/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs
+++ b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Models.Mapping
///
/// Use this class if your mapper configuration isn't also explicitly an ApplicationEventHandler.
///
- internal abstract class MapperConfiguration : ApplicationEventHandler, IMapperConfiguration
+ public abstract class MapperConfiguration : ApplicationEventHandler, IMapperConfiguration
{
public abstract void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext);
}
diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs
index 2f0e96f294..cb41f2b7f8 100644
--- a/src/Umbraco.Core/Models/Member.cs
+++ b/src/Umbraco.Core/Models/Member.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
+using Umbraco.Core.Logging;
namespace Umbraco.Core.Models
{
@@ -13,50 +15,104 @@ namespace Umbraco.Core.Models
public class Member : ContentBase, IMember
{
private readonly IMemberType _contentType;
- private string _contentTypeAlias;
+ private readonly string _contentTypeAlias;
private string _username;
private string _email;
- private string _password;
+ private string _rawPasswordValue;
private object _providerUserKey;
private Type _userTypeKey;
+ ///
+ /// Constructor for creating an empty Member object
+ ///
+ /// ContentType for the current Content object
+ public Member(IMemberType contentType)
+ : base("", -1, contentType, new PropertyCollection())
+ {
+ Mandate.ParameterNotNull(contentType, "contentType");
+
+ _contentTypeAlias = contentType.Alias;
+ _contentType = contentType;
+ IsApproved = true;
+
+ //this cannot be null but can be empty
+ _rawPasswordValue = "";
+ _email = "";
+ _username = "";
+ }
+
///
/// Constructor for creating a Member object
///
/// Name of the content
/// ContentType for the current Content object
public Member(string name, IMemberType contentType)
+ : this(contentType)
+ {
+ Mandate.ParameterNotNull(contentType, "contentType");
+ Mandate.ParameterNotNullOrEmpty(name, "name");
+
+ _contentTypeAlias = contentType.Alias;
+ _contentType = contentType;
+ IsApproved = true;
+
+ //this cannot be null but can be empty
+ _rawPasswordValue = "";
+ _email = "";
+ _username = "";
+ }
+
+ ///
+ /// Constructor for creating a Member object
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Member(string name, string email, string username, IMemberType contentType)
: base(name, -1, contentType, new PropertyCollection())
- {
- _contentType = contentType;
- }
-
- public Member(string name, string email, string username, string password, int parentId, IMemberType contentType)
- : base(name, parentId, contentType, new PropertyCollection())
{
Mandate.ParameterNotNull(contentType, "contentType");
+ Mandate.ParameterNotNullOrEmpty(name, "name");
+ Mandate.ParameterNotNullOrEmpty(email, "email");
+ Mandate.ParameterNotNullOrEmpty(username, "username");
+ _contentTypeAlias = contentType.Alias;
_contentType = contentType;
_email = email;
_username = username;
- _password = password;
+ IsApproved = true;
+
+ //this cannot be null but can be empty
+ _rawPasswordValue = "";
}
- public Member(string name, string email, string username, string password, IContentBase parent, IMemberType contentType)
- : base(name, parent, contentType, new PropertyCollection())
+ ///
+ /// Constructor for creating a Member object
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The password value passed in to this parameter should be the encoded/encrypted/hashed format of the member's password
+ ///
+ ///
+ public Member(string name, string email, string username, string rawPasswordValue, IMemberType contentType)
+ : base(name, -1, contentType, new PropertyCollection())
{
Mandate.ParameterNotNull(contentType, "contentType");
+ _contentTypeAlias = contentType.Alias;
_contentType = contentType;
_email = email;
_username = username;
- _password = password;
+ _rawPasswordValue = rawPasswordValue;
+ IsApproved = true;
}
- private static readonly PropertyInfo DefaultContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeAlias);
private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo(x => x.Username);
private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email);
- private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo(x => x.Password);
+ private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo(x => x.RawPasswordValue);
private static readonly PropertyInfo ProviderUserKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKey);
private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType);
@@ -95,19 +151,19 @@ namespace Umbraco.Core.Models
}
///
- /// Gets or sets the Password
+ /// Gets or sets the raw password value
///
[DataMember]
- public string Password
+ public string RawPasswordValue
{
- get { return _password; }
+ get { return _rawPasswordValue; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
- _password = value;
- return _password;
- }, _password, PasswordSelector);
+ _rawPasswordValue = value;
+ return _rawPasswordValue;
+ }, _rawPasswordValue, PasswordSelector);
}
}
@@ -125,7 +181,7 @@ namespace Umbraco.Core.Models
/// Gets or sets the Password Question
///
///
- /// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias
+ /// Alias: umbracoMemberPasswordRetrievalQuestion
/// Part of the standard properties collection.
///
[IgnoreDataMember]
@@ -133,34 +189,50 @@ namespace Umbraco.Core.Models
{
get
{
+ var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.PasswordQuestion, "PasswordQuestion", default(string));
+ if (a.Success == false) return a.Result;
+
return Properties[Constants.Conventions.Member.PasswordQuestion].Value == null
? string.Empty
: Properties[Constants.Conventions.Member.PasswordQuestion].Value.ToString();
}
set
{
+ if (WarnIfPropertyTypeNotFoundOnSet(
+ Constants.Conventions.Member.PasswordQuestion,
+ "PasswordQuestion") == false) return;
+
Properties[Constants.Conventions.Member.PasswordQuestion].Value = value;
}
}
///
- /// Gets or sets the Password Answer
+ /// Gets or sets the raw password answer value
///
///
- /// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias
+ /// For security reasons this value should be encrypted, the encryption process is handled by the memberhip provider
+ /// Alias: umbracoMemberPasswordRetrievalAnswer
+ ///
/// Part of the standard properties collection.
///
[IgnoreDataMember]
- public string PasswordAnswer
+ public string RawPasswordAnswerValue
{
get
{
+ var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.PasswordAnswer, "PasswordAnswer", default(string));
+ if (a.Success == false) return a.Result;
+
return Properties[Constants.Conventions.Member.PasswordAnswer].Value == null
? string.Empty
: Properties[Constants.Conventions.Member.PasswordAnswer].Value.ToString();
}
set
{
+ if (WarnIfPropertyTypeNotFoundOnSet(
+ Constants.Conventions.Member.PasswordAnswer,
+ "PasswordAnswer") == false) return;
+
Properties[Constants.Conventions.Member.PasswordAnswer].Value = value;
}
}
@@ -169,7 +241,7 @@ namespace Umbraco.Core.Models
/// Gets or set the comments for the member
///
///
- /// Alias: umbracoCommentPropertyTypeAlias
+ /// Alias: umbracoMemberComments
/// Part of the standard properties collection.
///
[IgnoreDataMember]
@@ -177,12 +249,19 @@ namespace Umbraco.Core.Models
{
get
{
+ var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.Comments, "Comments", default(string));
+ if (a.Success == false) return a.Result;
+
return Properties[Constants.Conventions.Member.Comments].Value == null
? string.Empty
: Properties[Constants.Conventions.Member.Comments].Value.ToString();
}
set
{
+ if (WarnIfPropertyTypeNotFoundOnSet(
+ Constants.Conventions.Member.Comments,
+ "Comments") == false) return;
+
Properties[Constants.Conventions.Member.Comments].Value = value;
}
}
@@ -191,7 +270,7 @@ namespace Umbraco.Core.Models
/// Gets or sets a boolean indicating whether the Member is approved
///
///
- /// Alias: umbracoApprovePropertyTypeAlias
+ /// Alias: umbracoMemberApproved
/// Part of the standard properties collection.
///
[IgnoreDataMember]
@@ -199,17 +278,25 @@ namespace Umbraco.Core.Models
{
get
{
- if (Properties[Constants.Conventions.Member.IsApproved].Value == null)
- return default(bool);
+ var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsApproved, "IsApproved",
+ //This is the default value if the prop is not found
+ true);
+ if (a.Success == false) return a.Result;
- if (Properties[Constants.Conventions.Member.IsApproved].Value is bool)
- return (bool)Properties[Constants.Conventions.Member.IsApproved].Value;
-
- //TODO: Use TryConvertTo instead
- return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsApproved].Value, typeof(bool));
+ var tryConvert = Properties[Constants.Conventions.Member.IsApproved].Value.TryConvertTo();
+ if (tryConvert.Success)
+ {
+ return tryConvert.Result;
+ }
+ //if the property exists but it cannot be converted, we will assume true
+ return true;
}
set
{
+ if (WarnIfPropertyTypeNotFoundOnSet(
+ Constants.Conventions.Member.IsApproved,
+ "IsApproved") == false) return;
+
Properties[Constants.Conventions.Member.IsApproved].Value = value;
}
}
@@ -218,7 +305,7 @@ namespace Umbraco.Core.Models
/// Gets or sets a boolean indicating whether the Member is locked out
///
///
- /// Alias: umbracoLockPropertyTypeAlias
+ /// Alias: umbracoMemberLockedOut
/// Part of the standard properties collection.
///
[IgnoreDataMember]
@@ -226,17 +313,23 @@ namespace Umbraco.Core.Models
{
get
{
- if (Properties[Constants.Conventions.Member.IsLockedOut].Value == null)
- return default(bool);
-
- if (Properties[Constants.Conventions.Member.IsLockedOut].Value is bool)
- return (bool)Properties[Constants.Conventions.Member.IsLockedOut].Value;
-
+ var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsLockedOut, "IsLockedOut", false);
+ if (a.Success == false) return a.Result;
+
+ var tryConvert = Properties[Constants.Conventions.Member.IsLockedOut].Value.TryConvertTo();
+ if (tryConvert.Success)
+ {
+ return tryConvert.Result;
+ }
+ return false;
//TODO: Use TryConvertTo instead
- return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsLockedOut].Value, typeof(bool));
}
set
{
+ if (WarnIfPropertyTypeNotFoundOnSet(
+ Constants.Conventions.Member.IsLockedOut,
+ "IsLockedOut") == false) return;
+
Properties[Constants.Conventions.Member.IsLockedOut].Value = value;
}
}
@@ -245,7 +338,7 @@ namespace Umbraco.Core.Models
/// Gets or sets the date for last login
///
///
- /// Alias: umbracoLastLoginPropertyTypeAlias
+ /// Alias: umbracoMemberLastLogin
/// Part of the standard properties collection.
///
[IgnoreDataMember]
@@ -253,17 +346,23 @@ namespace Umbraco.Core.Models
{
get
{
- if (Properties[Constants.Conventions.Member.LastLoginDate].Value == null)
- return default(DateTime);
-
- if (Properties[Constants.Conventions.Member.LastLoginDate].Value is DateTime)
- return (DateTime)Properties[Constants.Conventions.Member.LastLoginDate].Value;
-
+ var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLoginDate, "LastLoginDate", default(DateTime));
+ if (a.Success == false) return a.Result;
+
+ var tryConvert = Properties[Constants.Conventions.Member.LastLoginDate].Value.TryConvertTo();
+ if (tryConvert.Success)
+ {
+ return tryConvert.Result;
+ }
+ return default(DateTime);
//TODO: Use TryConvertTo instead
- return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLoginDate].Value, typeof(DateTime));
}
set
{
+ if (WarnIfPropertyTypeNotFoundOnSet(
+ Constants.Conventions.Member.LastLoginDate,
+ "LastLoginDate") == false) return;
+
Properties[Constants.Conventions.Member.LastLoginDate].Value = value;
}
}
@@ -272,7 +371,7 @@ namespace Umbraco.Core.Models
/// Gest or sets the date for last password change
///
///
- /// Alias: umbracoMemberLastPasswordChange
+ /// Alias: umbracoMemberLastPasswordChangeDate
/// Part of the standard properties collection.
///
[IgnoreDataMember]
@@ -280,17 +379,23 @@ namespace Umbraco.Core.Models
{
get
{
- if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value == null)
- return default(DateTime);
-
- if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value is DateTime)
- return (DateTime)Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value;
+ var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastPasswordChangeDate, "LastPasswordChangeDate", default(DateTime));
+ if (a.Success == false) return a.Result;
+ var tryConvert = Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value.TryConvertTo();
+ if (tryConvert.Success)
+ {
+ return tryConvert.Result;
+ }
+ return default(DateTime);
//TODO: Use TryConvertTo instead
- return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value, typeof(DateTime));
}
set
{
+ if (WarnIfPropertyTypeNotFoundOnSet(
+ Constants.Conventions.Member.LastPasswordChangeDate,
+ "LastPasswordChangeDate") == false) return;
+
Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value = value;
}
}
@@ -299,7 +404,7 @@ namespace Umbraco.Core.Models
/// Gets or sets the date for when Member was locked out
///
///
- /// Alias: umbracoMemberLastLockout
+ /// Alias: umbracoMemberLastLockoutDate
/// Part of the standard properties collection.
///
[IgnoreDataMember]
@@ -307,17 +412,23 @@ namespace Umbraco.Core.Models
{
get
{
- if (Properties[Constants.Conventions.Member.LastLockoutDate].Value == null)
- return default(DateTime);
-
- if (Properties[Constants.Conventions.Member.LastLockoutDate].Value is DateTime)
- return (DateTime)Properties[Constants.Conventions.Member.LastLockoutDate].Value;
-
+ var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLockoutDate, "LastLockoutDate", default(DateTime));
+ if (a.Success == false) return a.Result;
+
+ var tryConvert = Properties[Constants.Conventions.Member.LastLockoutDate].Value.TryConvertTo();
+ if (tryConvert.Success)
+ {
+ return tryConvert.Result;
+ }
+ return default(DateTime);
//TODO: Use TryConvertTo instead
- return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLockoutDate].Value, typeof(DateTime));
}
set
{
+ if (WarnIfPropertyTypeNotFoundOnSet(
+ Constants.Conventions.Member.LastLockoutDate,
+ "LastLockoutDate") == false) return;
+
Properties[Constants.Conventions.Member.LastLockoutDate].Value = value;
}
}
@@ -327,7 +438,7 @@ namespace Umbraco.Core.Models
/// This is the number of times the password was entered incorrectly upon login.
///
///
- /// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias
+ /// Alias: umbracoMemberFailedPasswordAttempts
/// Part of the standard properties collection.
///
[IgnoreDataMember]
@@ -335,18 +446,24 @@ namespace Umbraco.Core.Models
{
get
{
- if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value == null)
- return default(int);
-
- if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value is int)
- return (int)Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value;
-
+ var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.FailedPasswordAttempts, "FailedPasswordAttempts", 0);
+ if (a.Success == false) return a.Result;
+
+ var tryConvert = Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value.TryConvertTo();
+ if (tryConvert.Success)
+ {
+ return tryConvert.Result;
+ }
+ return default(int);
//TODO: Use TryConvertTo instead
- return (int)Convert.ChangeType(Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value, typeof(int));
}
set
{
- Properties[Constants.Conventions.Member.LastLockoutDate].Value = value;
+ if (WarnIfPropertyTypeNotFoundOnSet(
+ Constants.Conventions.Member.FailedPasswordAttempts,
+ "FailedPasswordAttempts") == false) return;
+
+ Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value = value;
}
}
@@ -357,14 +474,6 @@ namespace Umbraco.Core.Models
public virtual string ContentTypeAlias
{
get { return _contentTypeAlias; }
- internal set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _contentTypeAlias = value;
- return _contentTypeAlias;
- }, _contentTypeAlias, DefaultContentTypeAliasSelector);
- }
}
///
@@ -377,7 +486,7 @@ namespace Umbraco.Core.Models
/// membership provider.
///
[DataMember]
- internal virtual object ProviderUserKey
+ public virtual object ProviderUserKey
{
get
{
@@ -460,5 +569,73 @@ namespace Umbraco.Core.Models
internal bool BoolPropertyValue { get; set; }
internal DateTime DateTimePropertyValue { get; set; }
internal string PropertyTypeAlias { get; set; }
+
+ private Attempt WarnIfPropertyTypeNotFoundOnGet(string propertyAlias, string propertyName, T defaultVal)
+ {
+ Action doLog = () => LogHelper.Warn(
+ "Trying to access the '"
+ + propertyName
+ + "' property on "
+ + typeof(Member)
+ + " but the "
+ + propertyAlias
+ + " property does not exist on the member type so a default value is returned. Ensure that you have a property type with alias: "
+ + propertyAlias
+ + " configured on your member type in order to use the '"
+ + propertyName
+ + "' property on the model correctly.");
+
+ //if the property doesn't exist, then do the logging and return a failure
+ if (Properties.Contains(propertyAlias) == false)
+ {
+ //we'll put a warn in the log if this entity has been persisted
+ if (HasIdentity)
+ {
+ doLog();
+ }
+ return Attempt.Fail(defaultVal);
+ }
+
+ //if the property doesn't have an identity but we do, then do logging and return failure
+ var prop = Properties.Single(x => x.Alias == propertyAlias);
+ if (prop.HasIdentity == false && HasIdentity)
+ {
+ doLog();
+ return Attempt.Fail(defaultVal);
+ }
+
+ return Attempt.Succeed();
+ }
+
+ private bool WarnIfPropertyTypeNotFoundOnSet(string propertyAlias, string propertyname)
+ {
+ Action doLog = () => LogHelper.Warn("An attempt was made to set a value on the property '"
+ + propertyname
+ + "' on type "
+ + typeof(Member)
+ + " but the property type "
+ + propertyAlias
+ + " does not exist on the member type, ensure that this property type exists so that setting this property works correctly.");
+
+ //if the property doesn't exist, then do the logging and return a failure
+ if (Properties.Contains(propertyAlias) == false)
+ {
+ 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();
+ return false;
+ }
+
+ return true;
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/MemberGroup.cs b/src/Umbraco.Core/Models/MemberGroup.cs
new file mode 100644
index 0000000000..e52448a11d
--- /dev/null
+++ b/src/Umbraco.Core/Models/MemberGroup.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.Serialization;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Represents a member type
+ ///
+ [Serializable]
+ [DataContract(IsReference = true)]
+ public class MemberGroup : Entity, IMemberGroup
+ {
+ public MemberGroup()
+ {
+ AdditionalData = new Dictionary();
+ }
+
+ private string _name;
+ private int _creatorId;
+
+ private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
+ private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => x.CreatorId);
+
+ [DataMember]
+ public string Name
+ {
+ get { return _name; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ if (_name != value)
+ {
+ //if the name has changed, add the value to the additional data,
+ //this is required purely for event handlers to know the previous name of the group
+ //so we can keep the public access up to date.
+ AdditionalData["previousName"] = _name;
+ }
+
+ _name = value;
+ return _name;
+ }, _name, NameSelector);
+ }
+ }
+
+ public int CreatorId
+ {
+ get { return _creatorId; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _creatorId = value;
+ return _creatorId;
+ }, _creatorId, CreatorIdSelector);
+ }
+ }
+
+ public IDictionary AdditionalData { get; private set; }
+
+ ///
+ /// Method to call when Entity is being saved
+ ///
+ /// Created date is set and a Unique key is assigned
+ internal override void AddingEntity()
+ {
+ base.AddingEntity();
+
+ if (Key == Guid.Empty)
+ Key = Guid.NewGuid();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs
index c361891fa0..527e379d17 100644
--- a/src/Umbraco.Core/Models/MemberType.cs
+++ b/src/Umbraco.Core/Models/MemberType.cs
@@ -14,37 +14,50 @@ namespace Umbraco.Core.Models
public class MemberType : ContentTypeCompositionBase, IMemberType
{
//Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId
- private IDictionary> _memberTypePropertyTypes;
+ private string _alias;
public MemberType(int parentId) : base(parentId)
{
- _memberTypePropertyTypes = new Dictionary>();
+ MemberTypePropertyTypes = new Dictionary();
}
public MemberType(IContentTypeComposition parent) : base(parent)
{
- _memberTypePropertyTypes = new Dictionary>();
+ MemberTypePropertyTypes = new Dictionary();
}
- private static readonly PropertyInfo MemberTypePropertyTypesSelector = ExpressionHelper.GetPropertyInfo>>(x => x.MemberTypePropertyTypes);
+ private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias);
///
- /// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, PropertyTypeId) by the PropertyTypes' alias.
+ /// The Alias of the ContentType
///
[DataMember]
- internal IDictionary> MemberTypePropertyTypes
+ public override string Alias
{
- get { return _memberTypePropertyTypes; }
+ get { return _alias; }
set
{
+ //NOTE: WE are overriding this because we don't want to do a ToSafeAlias when the alias is the special case of
+ // "_umbracoSystemDefaultProtectType" which is used internally, currently there is an issue with the safe alias as it strips
+ // leading underscores which we don't want in this case.
+ // see : http://issues.umbraco.org/issue/U4-3968
+
SetPropertyValueAndDetectChanges(o =>
{
- _memberTypePropertyTypes = value;
- return _memberTypePropertyTypes;
- }, _memberTypePropertyTypes, MemberTypePropertyTypesSelector);
+ _alias = value == "_umbracoSystemDefaultProtectType"
+ ? value
+ : (value == null ? string.Empty : value.ToSafeAlias() );
+ return _alias;
+ }, _alias, AliasSelector);
}
}
+ ///
+ /// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile) by the PropertyTypes' alias.
+ ///
+ [DataMember]
+ internal IDictionary MemberTypePropertyTypes { get; private set; }
+
///
/// Gets a boolean indicating whether a Property is editable by the Member.
///
@@ -54,7 +67,7 @@ namespace Umbraco.Core.Models
{
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
{
- return MemberTypePropertyTypes[propertyTypeAlias].Item1;
+ return MemberTypePropertyTypes[propertyTypeAlias].IsEditable;
}
return false;
@@ -69,7 +82,7 @@ namespace Umbraco.Core.Models
{
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
{
- return MemberTypePropertyTypes[propertyTypeAlias].Item2;
+ return MemberTypePropertyTypes[propertyTypeAlias].IsVisible;
}
return false;
@@ -84,13 +97,11 @@ namespace Umbraco.Core.Models
{
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
{
- var tuple = MemberTypePropertyTypes[propertyTypeAlias];
- MemberTypePropertyTypes[propertyTypeAlias] = new Tuple(value, tuple.Item2, tuple.Item3);
+ MemberTypePropertyTypes[propertyTypeAlias].IsEditable = value;
}
else
{
- var propertyType = PropertyTypes.First(x => x.Alias.Equals(propertyTypeAlias));
- var tuple = new Tuple(value, false, propertyType.Id);
+ var tuple = new MemberTypePropertyProfileAccess(false, value);
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
}
}
@@ -104,13 +115,11 @@ namespace Umbraco.Core.Models
{
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
{
- var tuple = MemberTypePropertyTypes[propertyTypeAlias];
- MemberTypePropertyTypes[propertyTypeAlias] = new Tuple(tuple.Item1, value, tuple.Item3);
+ MemberTypePropertyTypes[propertyTypeAlias].IsVisible = value;
}
else
{
- var propertyType = PropertyTypes.First(x => x.Alias.Equals(propertyTypeAlias));
- var tuple = new Tuple(false, value, propertyType.Id);
+ var tuple = new MemberTypePropertyProfileAccess(value, false);
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
}
}
diff --git a/src/Umbraco.Core/Models/MemberTypePropertyProfileAccess.cs b/src/Umbraco.Core/Models/MemberTypePropertyProfileAccess.cs
new file mode 100644
index 0000000000..fa9e0b7307
--- /dev/null
+++ b/src/Umbraco.Core/Models/MemberTypePropertyProfileAccess.cs
@@ -0,0 +1,17 @@
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Used to track the property types that are visible/editable on member profiles
+ ///
+ internal class MemberTypePropertyProfileAccess
+ {
+ public MemberTypePropertyProfileAccess(bool isVisible, bool isEditable)
+ {
+ IsVisible = isVisible;
+ IsEditable = isEditable;
+ }
+
+ public bool IsVisible { get; set; }
+ public bool IsEditable { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Membership/EntityPermission.cs b/src/Umbraco.Core/Models/Membership/EntityPermission.cs
index c417a4985c..175e571fdf 100644
--- a/src/Umbraco.Core/Models/Membership/EntityPermission.cs
+++ b/src/Umbraco.Core/Models/Membership/EntityPermission.cs
@@ -5,14 +5,14 @@
///
public class EntityPermission
{
- public EntityPermission(object userId, int entityId, string[] assignedPermissions)
+ public EntityPermission(int userId, int entityId, string[] assignedPermissions)
{
UserId = userId;
EntityId = entityId;
AssignedPermissions = assignedPermissions;
}
- public object UserId { get; private set; }
+ public int UserId { get; private set; }
public int EntityId { get; private set; }
///
diff --git a/src/Umbraco.Core/Models/Membership/IMembershipUser.cs b/src/Umbraco.Core/Models/Membership/IMembershipUser.cs
index 422b47da22..b304f72777 100644
--- a/src/Umbraco.Core/Models/Membership/IMembershipUser.cs
+++ b/src/Umbraco.Core/Models/Membership/IMembershipUser.cs
@@ -4,23 +4,42 @@ using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models.Membership
{
- internal interface IMembershipUser : IMembershipUserId, IAggregateRoot
- {
- /*new object Id { get; set; }*/
+ public interface IMembershipUser : IAggregateRoot
+ {
+ object ProviderUserKey { get; set; }
string Username { get; set; }
string Email { get; set; }
- string Password { get; set; }
+
+ ///
+ /// Gets or sets the raw password value
+ ///
+ string RawPasswordValue { get; set; }
+
string PasswordQuestion { get; set; }
- string PasswordAnswer { get; set; }
+
+ ///
+ /// Gets or sets the raw password answer value
+ ///
+ string RawPasswordAnswerValue { get; set; }
+
string Comments { get; set; }
bool IsApproved { get; set; }
- bool IsOnline { get; set; }
bool IsLockedOut { get; set; }
DateTime LastLoginDate { get; set; }
DateTime LastPasswordChangeDate { get; set; }
DateTime LastLockoutDate { get; set; }
- object ProfileId { get; set; }
- IEnumerable
/// Will be left internal until a proper Membership implementation is part of the roadmap
- internal interface IUser : IMembershipUser, IUserProfile
+ public interface IUser : IMembershipUser
{
- new object Id { get; set; }
- //string Name { get; set; }
+ string Name { get; set; }
int SessionTimeout { get; set; }
int StartContentId { get; set; }
int StartMediaId { get; set; }
string Language { get; set; }
- bool NoConsole { get; set; }
- IUserType UserType { get; }
-
///
- /// The default permissions for the user
+ /// Gets/sets the user type for the user
+ ///
+ IUserType UserType { get; set; }
+
+ ///
+ /// The default permission set for the user
///
///
- /// The default permissions are assigned to the user object based on the user type's default permissions
- ///
- string DefaultPermissions { get; }
- }
+ /// Currently in umbraco each permission is a single char but with an Enumerable{string} collection this allows for flexible changes to this in the future
+ ///
+ IEnumerable DefaultPermissions { get; set; }
- internal interface IUserProfile : IProfile
- {
IEnumerable AllowedSections { get; }
void RemoveAllowedSection(string sectionAlias);
void AddAllowedSection(string sectionAlias);
+
+ ///
+ /// Exposes the basic profile data
+ ///
+ IProfile ProfileData { get; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Membership/IUserType.cs b/src/Umbraco.Core/Models/Membership/IUserType.cs
index aa837bf890..fe678afd2b 100644
--- a/src/Umbraco.Core/Models/Membership/IUserType.cs
+++ b/src/Umbraco.Core/Models/Membership/IUserType.cs
@@ -1,13 +1,28 @@
+using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Models.Membership
{
- internal interface IUserType : IAggregateRoot
+ public interface IUserType : IAggregateRoot
{
+ ///
+ /// The user type alias
+ ///
string Alias { get; set; }
+
+ ///
+ /// The user type name
+ ///
string Name { get; set; }
- string Permissions { get; set; }
+
+ ///
+ /// The set of default permissions for the user type
+ ///
+ ///
+ /// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
+ ///
+ IEnumerable Permissions { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Membership/MemberCountType.cs b/src/Umbraco.Core/Models/Membership/MemberCountType.cs
new file mode 100644
index 0000000000..89b428a827
--- /dev/null
+++ b/src/Umbraco.Core/Models/Membership/MemberCountType.cs
@@ -0,0 +1,13 @@
+namespace Umbraco.Core.Models.Membership
+{
+ ///
+ /// The types of members to count
+ ///
+ public enum MemberCountType
+ {
+ All,
+ Online,
+ LockedOut,
+ Approved
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs b/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs
similarity index 65%
rename from src/Umbraco.Core/Models/Membership/MembershipExtensions.cs
rename to src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs
index df0ee1529a..6008c0ae02 100644
--- a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs
+++ b/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs
@@ -1,20 +1,21 @@
using System;
using System.Web.Security;
+using Umbraco.Core.Security;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models.Membership
{
- internal static class MembershipExtensions
+ internal static class MembershipUserExtensions
{
- internal static MembershipUser AsConcreteMembershipUser(this IMember member)
+ internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName)
{
- var membershipMember = new UmbracoMembershipMember(member);
+ var membershipMember = new UmbracoMembershipMember(member, providerName);
return membershipMember;
}
- internal static IMember AsIMember(this MembershipUser membershipMember)
+ internal static IMembershipUser AsIMember(this UmbracoMembershipMember membershipMember)
{
- var member = membershipMember as UmbracoMembershipMember;
+ var member = membershipMember;
if (member != null)
{
return member.Member;
@@ -32,11 +33,12 @@ namespace Umbraco.Core.Models.Membership
{
if (_scenario.HasValue == false)
{
- if (System.Web.Security.Membership.Provider.Name == Constants.Conventions.Member.UmbracoMemberProviderName)
+ var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
+ if (provider.IsUmbracoMembershipProvider())
{
return MembershipScenario.NativeUmbraco;
}
- var memberType = ApplicationContext.Current.Services.MemberTypeService.GetMemberType(Constants.Conventions.MemberTypes.Member);
+ var memberType = ApplicationContext.Current.Services.MemberTypeService.Get(Constants.Conventions.MemberTypes.DefaultAlias);
return memberType != null
? MembershipScenario.CustomProviderWithUmbracoLink
: MembershipScenario.StandaloneCustomProvider;
diff --git a/src/Umbraco.Core/Models/Membership/Profile.cs b/src/Umbraco.Core/Models/Membership/Profile.cs
deleted file mode 100644
index b3959bb9cd..0000000000
--- a/src/Umbraco.Core/Models/Membership/Profile.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-using System;
-using System.Reflection;
-using System.Runtime.Serialization;
-using Umbraco.Core.Models.EntityBase;
-
-namespace Umbraco.Core.Models.Membership
-{
- ///
- /// Represents a Profile which is shared between Members and Users
- ///
- [Serializable]
- [DataContract(IsReference = true)]
- internal class Profile : TracksChangesEntityBase, IProfile, IRememberBeingDirty
- {
- ///
- /// Initializes a new instance of the class.
- ///
- protected Profile()
- {
- ProviderUserKeyType = typeof(int);
- }
-
- public Profile(object id, string name)
- {
- ProviderUserKeyType = typeof(int);
- Id = id;
- Name = name;
- }
-
- private object _id;
- private string _name;
- private object _providerUserKey;
- private Type _userTypeKey;
-
- private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo(x => x.Id);
- private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
- private static readonly PropertyInfo ProviderUserKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKey);
- private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType);
-
- [DataMember]
- public virtual object Id
- {
- get
- {
- return _id;
- }
- set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _id = value;
- return _id;
- }, _id, IdSelector);
- }
- }
-
- [DataMember]
- public virtual string Name
- {
- get
- {
- return _name;
- }
- set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _name = value;
- return _name;
- }, _name, NameSelector);
- }
- }
-
- [IgnoreDataMember]
- public virtual object ProviderUserKey
- {
- get
- {
- return _providerUserKey;
- }
- set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _providerUserKey = value;
- return _id;
- }, _providerUserKey, ProviderUserKeySelector);
- }
- }
-
- ///
- /// Gets or sets the type of the provider user key.
- ///
- ///
- /// The type of the provider user key.
- ///
- [IgnoreDataMember]
- internal Type ProviderUserKeyType
- {
- get
- {
- return _userTypeKey;
- }
- private set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _userTypeKey = value;
- return _userTypeKey;
- }, _userTypeKey, UserTypeKeySelector);
- }
- }
-
- ///
- /// Sets the type of the provider user key.
- ///
- /// The type.
- internal void SetProviderUserKeyType(Type type)
- {
- ProviderUserKeyType = type;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs
index 4397f766c0..1b8c7f5393 100644
--- a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs
+++ b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs
@@ -1,66 +1,127 @@
-using System.Web.Security;
+using System;
+using System.Web.Security;
namespace Umbraco.Core.Models.Membership
{
- //TODO: THere's still a bunch of properties that don't exist in this use that need to be mapped somehow.
internal class UmbracoMembershipMember : MembershipUser
{
- private readonly IMember _member;
+ private readonly IMembershipUser _member;
+ private readonly string _userName;
+ private readonly object _providerUserKey;
+ private readonly string _passwordQuestion;
+ private readonly bool _isLockedOut;
+ private readonly DateTime _lastLockoutDate;
+ private readonly DateTime _creationDate;
+ private DateTime _lastLoginDate;
+ private readonly DateTime _lastPasswordChangedDate;
+ private readonly string _providerName;
+ private string _email;
+ private string _comment;
+ private bool _isApproved;
+ private DateTime _lastActivityDate;
- public UmbracoMembershipMember(IMember member)
+ //NOTE: We are only overriding the properties that matter, we don't override things like IsOnline since that is handled with the sub-class and the membership providers.
+
+ //NOTE: We are not calling the base constructor which will validate that a provider with the specified name exists which causes issues with unit tests. The ctor
+ // validation for that doesn't need to be there anyways (have checked the source).
+ public UmbracoMembershipMember(IMembershipUser member, string providerName)
{
_member = member;
+ //NOTE: We are copying the values here so that everything is consistent with how the underlying built-in ASP.Net membership user
+ // handles data! We don't want to do anything differently there but since we cannot use their ctor we'll need to handle this logic ourselves.
+ if (member.Username != null)
+ _userName = member.Username.Trim();
+ if (member.Email != null)
+ _email = member.Email.Trim();
+ if (member.PasswordQuestion != null)
+ _passwordQuestion = member.PasswordQuestion.Trim();
+ _providerName = providerName;
+ _providerUserKey = member.ProviderUserKey;
+ _comment = member.Comments;
+ _isApproved = member.IsApproved;
+ _isLockedOut = member.IsLockedOut;
+ _creationDate = member.CreateDate.ToUniversalTime();
+ _lastLoginDate = member.LastLoginDate.ToUniversalTime();
+ //TODO: We currently don't really have any place to store this data!!
+ _lastActivityDate = member.LastLoginDate.ToUniversalTime();
+ _lastPasswordChangedDate = member.LastPasswordChangeDate.ToUniversalTime();
+ _lastLockoutDate = member.LastLockoutDate.ToUniversalTime();
}
-
- internal IMember Member
+
+ internal IMembershipUser Member
{
get { return _member; }
}
- public override string Email
+ public override string UserName
{
- get { return _member.Email; }
- set { _member.Email = value; }
+ get { return _userName; }
}
public override object ProviderUserKey
{
- get { return _member.Key; }
+ get { return _providerUserKey; }
}
- public override System.DateTime CreationDate
+ public override string Email
{
- get { return _member.CreateDate; }
+ get { return _email; }
+ set { _email = value; }
}
- public override string UserName
+ public override string PasswordQuestion
{
- get { return _member.Username; }
+ get { return _passwordQuestion; }
}
public override string Comment
{
- get { return _member.Comments; }
- set { _member.Comments = value; }
+ get { return _comment; }
+ set { _comment = value; }
}
public override bool IsApproved
{
- get { return _member.IsApproved; }
- set { _member.IsApproved = value; }
+ get { return _isApproved; }
+ set { _isApproved = value; }
}
public override bool IsLockedOut
{
- get { return _member.IsLockedOut; }
+ get { return _isLockedOut; }
}
- public override System.DateTime LastLoginDate
+ public override DateTime LastLockoutDate
{
- get { return _member.LastLoginDate; }
- set { _member.LastLoginDate = value; }
+ get { return _lastLockoutDate; }
}
+ public override DateTime CreationDate
+ {
+ get { return _creationDate; }
+ }
+
+ public override DateTime LastLoginDate
+ {
+ get { return _lastLoginDate; }
+ set { _lastLoginDate = value; }
+ }
+
+ public override DateTime LastActivityDate
+ {
+ get { return _lastActivityDate; }
+ set { _lastActivityDate = value; }
+ }
+
+ public override DateTime LastPasswordChangedDate
+ {
+ get { return _lastPasswordChangedDate; }
+ }
+
+ public override string ProviderName
+ {
+ get { return _providerName; }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs
index 9840c25b98..2010161df0 100644
--- a/src/Umbraco.Core/Models/Membership/User.cs
+++ b/src/Umbraco.Core/Models/Membership/User.cs
@@ -1,7 +1,11 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
using System.Linq;
+using System.Reflection;
using System.Runtime.Serialization;
+using Umbraco.Core.Configuration;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Mappers;
@@ -16,97 +20,501 @@ namespace Umbraco.Core.Models.Membership
///
[Serializable]
[DataContract(IsReference = true)]
- internal class User : UserProfile, IUser
+ public class User : TracksChangesEntityBase, IUser
{
- private bool _hasIdentity;
-
public User(IUserType userType)
{
- Groups = new List { userType };
+ if (userType == null) throw new ArgumentNullException("userType");
+
+ _userType = userType;
+ _defaultPermissions = _userType.Permissions;
+ //Groups = new List { userType };
+ SessionTimeout = 60;
+ _sectionCollection = new ObservableCollection();
+ _addedSections = new List();
+ _removedSections = new List();
+ _language = GlobalSettings.DefaultUILanguage;
+ _sectionCollection.CollectionChanged += SectionCollectionChanged;
+ _isApproved = true;
+ _isLockedOut = false;
+ _startContentId = -1;
+ _startMediaId = -1;
+ //cannot be null
+ _rawPasswordValue = "";
}
+ public User(string name, string email, string username, string rawPasswordValue, IUserType userType)
+ : this(userType)
+ {
+ _name = name;
+ _email = email;
+ _username = username;
+ _rawPasswordValue = rawPasswordValue;
+ _isApproved = true;
+ _isLockedOut = false;
+ _startContentId = -1;
+ _startMediaId = -1;
+ }
+
+ private IUserType _userType;
+ private bool _hasIdentity;
+ private int _id;
+ private string _name;
+ private Type _userTypeKey;
+ private readonly List _addedSections;
+ private readonly List _removedSections;
+ private readonly ObservableCollection _sectionCollection;
+ private int _sessionTimeout;
+ private int _startContentId;
+ private int _startMediaId;
+
+ private string _username;
+ private string _email;
+ private string _rawPasswordValue;
+ private bool _isApproved;
+ private bool _isLockedOut;
+ private string _language;
+ private IEnumerable _defaultPermissions;
+ private bool _defaultToLiveEditing;
+
+ private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo(x => x.SessionTimeout);
+ private static readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartContentId);
+ private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartMediaId);
+ private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedSections);
+ private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo(x => x.Id);
+ private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
+ private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType);
+
+ private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo(x => x.Username);
+ private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email);
+ private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo(x => x.RawPasswordValue);
+ private static readonly PropertyInfo IsLockedOutSelector = ExpressionHelper.GetPropertyInfo(x => x.IsLockedOut);
+ private static readonly PropertyInfo IsApprovedSelector = ExpressionHelper.GetPropertyInfo(x => x.IsApproved);
+ private static readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.Language);
+ private static readonly PropertyInfo DefaultPermissionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.DefaultPermissions);
+ private static readonly PropertyInfo DefaultToLiveEditingSelector = ExpressionHelper.GetPropertyInfo(x => x.DefaultToLiveEditing);
+ private static readonly PropertyInfo HasIdentitySelector = ExpressionHelper.GetPropertyInfo(x => x.HasIdentity);
+ private static readonly PropertyInfo UserTypeSelector = ExpressionHelper.GetPropertyInfo(x => x.UserType);
+
#region Implementation of IEntity
[IgnoreDataMember]
- public bool HasIdentity { get { return Id != null || _hasIdentity; } }
-
- [IgnoreDataMember]
- int IEntity.Id
+ public bool HasIdentity
{
get
{
- return int.Parse(base.Id.ToString());
+ return _hasIdentity;
}
- set
+ protected set
{
- base.Id = value;
- _hasIdentity = true;
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _hasIdentity = value;
+ return _hasIdentity;
+ }, _hasIdentity, HasIdentitySelector);
}
}
+ [DataMember]
+ public int Id
+ {
+ get
+ {
+ return _id;
+ }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _id = value;
+ HasIdentity = true; //set the has Identity
+ return _id;
+ }, _id, IdSelector);
+ }
+ }
+
+ //this doesn't get used
+ [IgnoreDataMember]
public Guid Key { get; set; }
#endregion
#region Implementation of IMembershipUser
+ [IgnoreDataMember]
+ public object ProviderUserKey
+ {
+ get { return Id; }
+ set { throw new NotSupportedException("Cannot set the provider user key for a user"); }
+ }
+
+ ///
+ /// Gets or sets the type of the provider user key.
+ ///
+ ///
+ /// The type of the provider user key.
+ ///
+ [IgnoreDataMember]
+ internal Type ProviderUserKeyType
+ {
+ get
+ {
+ return _userTypeKey;
+ }
+ private set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _userTypeKey = value;
+ return _userTypeKey;
+ }, _userTypeKey, UserTypeKeySelector);
+ }
+ }
+
+ ///
+ /// Sets the type of the provider user key.
+ ///
+ /// The type.
+ internal void SetProviderUserKeyType(Type type)
+ {
+ ProviderUserKeyType = type;
+ }
+
[DataMember]
- public string Username { get; set; }
+ public string Username
+ {
+ get { return _username; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _username = value;
+ return _username;
+ }, _username, UsernameSelector);
+ }
+ }
[DataMember]
- public string Email { get; set; }
+ public string Email
+ {
+ get { return _email; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _email = value;
+ return _email;
+ }, _email, EmailSelector);
+ }
+ }
[DataMember]
- public string Password { get; set; }
+ public string RawPasswordValue
+ {
+ get { return _rawPasswordValue; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _rawPasswordValue = value;
+ return _rawPasswordValue;
+ }, _rawPasswordValue, PasswordSelector);
+ }
+ }
+
[DataMember]
+ public bool IsApproved
+ {
+ get { return _isApproved; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _isApproved = value;
+ return _isApproved;
+ }, _isApproved, IsApprovedSelector);
+ }
+ }
+
+ [DataMember]
+ public bool IsLockedOut
+ {
+ get { return _isLockedOut; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _isLockedOut = value;
+ return _isLockedOut;
+ }, _isLockedOut, IsLockedOutSelector);
+ }
+ }
+
+ //TODO: Figure out how to support all of this! - we cannot have NotImplementedExceptions because these get used by the IMembershipMemberService service so
+ // we'll just have them as generic get/set which don't interact with the db.
+
+ [IgnoreDataMember]
public string PasswordQuestion { get; set; }
- [DataMember]
- public string PasswordAnswer { get; set; }
- [DataMember]
+ [IgnoreDataMember]
+ public string RawPasswordAnswerValue { get; set; }
+ [IgnoreDataMember]
public string Comments { get; set; }
- [DataMember]
- public bool IsApproved { get; set; }
- [DataMember]
- public bool IsOnline { get; set; }
- [DataMember]
- public bool IsLockedOut { get; set; }
- [DataMember]
+ [IgnoreDataMember]
public DateTime CreateDate { get; set; }
- [DataMember]
+ [IgnoreDataMember]
public DateTime UpdateDate { get; set; }
- [DataMember]
+ [IgnoreDataMember]
public DateTime LastLoginDate { get; set; }
- [DataMember]
+ [IgnoreDataMember]
public DateTime LastPasswordChangeDate { get; set; }
- [DataMember]
+ [IgnoreDataMember]
public DateTime LastLockoutDate { get; set; }
-
- [DataMember]
- public object ProfileId { get; set; }
- [DataMember]
- public IEnumerable Groups { get; set; }
-
+ [IgnoreDataMember]
+ public int FailedPasswordAttempts { get; set; }
+
#endregion
-
+
#region Implementation of IUser
[DataMember]
- public string Language { get; set; }
+ public string Name
+ {
+ get { return _name; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _name = value;
+ return _name;
+ }, _name, NameSelector);
+ }
+ }
+
+ public IEnumerable AllowedSections
+ {
+ get { return _sectionCollection; }
+ }
+
+ public void RemoveAllowedSection(string sectionAlias)
+ {
+ _sectionCollection.Remove(sectionAlias);
+ }
+
+ public void AddAllowedSection(string sectionAlias)
+ {
+ if (_sectionCollection.Contains(sectionAlias) == false)
+ {
+ _sectionCollection.Add(sectionAlias);
+ }
+ }
+
+ public IProfile ProfileData
+ {
+ get { return new UserProfile(this); }
+ }
+
+ ///
+ /// Used internally to check if we need to add a section in the repository to the db
+ ///
+ internal IEnumerable AddedSections
+ {
+ get { return _addedSections; }
+ }
+
+ ///
+ /// Used internally to check if we need to remove a section in the repository to the db
+ ///
+ internal IEnumerable RemovedSections
+ {
+ get { return _removedSections; }
+ }
+
+ ///
+ /// Gets or sets the session timeout.
+ ///
+ ///
+ /// The session timeout.
+ ///
+ [DataMember]
+ public int SessionTimeout
+ {
+ get { return _sessionTimeout; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _sessionTimeout = value;
+ return _sessionTimeout;
+ }, _sessionTimeout, SessionTimeoutSelector);
+ }
+ }
+
+ ///
+ /// Gets or sets the start content id.
+ ///
+ ///
+ /// The start content id.
+ ///
+ [DataMember]
+ public int StartContentId
+ {
+ get { return _startContentId; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _startContentId = value;
+ return _startContentId;
+ }, _startContentId, StartContentIdSelector);
+ }
+ }
+
+ ///
+ /// Gets or sets the start media id.
+ ///
+ ///
+ /// The start media id.
+ ///
+ [DataMember]
+ public int StartMediaId
+ {
+ get { return _startMediaId; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _startMediaId = value;
+ return _startMediaId;
+ }, _startMediaId, StartMediaIdSelector);
+ }
+ }
[DataMember]
- public string DefaultPermissions { get; set; }
-
+ public string Language
+ {
+ get { return _language; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _language = value;
+ return _language;
+ }, _language, LanguageSelector);
+ }
+ }
+
[DataMember]
- public bool NoConsole { get; set; }
+ public IEnumerable DefaultPermissions
+ {
+ get { return _defaultPermissions; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _defaultPermissions = value;
+ return _defaultPermissions;
+ }, _defaultPermissions, DefaultPermissionsSelector);
+ }
+ }
+
+ [IgnoreDataMember]
+ internal bool DefaultToLiveEditing
+ {
+ get { return _defaultToLiveEditing; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _defaultToLiveEditing = value;
+ return _defaultToLiveEditing;
+ }, _defaultToLiveEditing, DefaultToLiveEditingSelector);
+ }
+ }
[IgnoreDataMember]
public IUserType UserType
{
- get
+ get { return _userType; }
+ set
{
- var type = Groups.FirstOrDefault();
- return type as IUserType;
+ if (value.HasIdentity == false)
+ {
+ throw new InvalidOperationException("Cannot assign a User Type that has not been persisted");
+ }
+
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _userType = value;
+ return _userType;
+ }, _userType, UserTypeSelector);
}
}
#endregion
+
+ ///
+ /// Whenever resetting occurs, clear the remembered add/removed collections, even if
+ /// rememberPreviouslyChangedProperties is true, the AllowedSections property will still
+ /// be flagged as dirty.
+ ///
+ ///
+ public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
+ {
+ _addedSections.Clear();
+ _removedSections.Clear();
+ base.ResetDirtyProperties(rememberPreviouslyChangedProperties);
+ }
+
+ ///
+ /// Handles the collection changed event in order for us to flag the AllowedSections property as changed
+ ///
+ ///
+ ///
+ void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ OnPropertyChanged(AllowedSectionsSelector);
+
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ //remove from the removed/added sections (since people could add/remove all they want in one request)
+ _removedSections.RemoveAll(s => s == e.NewItems.Cast().First());
+ _addedSections.RemoveAll(s => s == e.NewItems.Cast().First());
+
+ //add to the added sections
+ _addedSections.Add(e.NewItems.Cast().First());
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ //remove from the removed/added sections (since people could add/remove all they want in one request)
+ _removedSections.RemoveAll(s => s == e.OldItems.Cast().First());
+ _addedSections.RemoveAll(s => s == e.OldItems.Cast().First());
+
+ //add to the added sections
+ _removedSections.Add(e.OldItems.Cast().First());
+ }
+ }
+
+ ///
+ /// Internal class used to wrap the user in a profile
+ ///
+ private class UserProfile : IProfile
+ {
+ private readonly IUser _user;
+
+ public UserProfile(IUser user)
+ {
+ _user = user;
+ }
+
+ public object Id
+ {
+ get { return _user.Id; }
+ set { _user.Id = (int)value; }
+ }
+
+ public string Name
+ {
+ get { return _user.Name; }
+ set { _user.Name = value; }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Membership/UserProfile.cs b/src/Umbraco.Core/Models/Membership/UserProfile.cs
deleted file mode 100644
index 61a0d68ba7..0000000000
--- a/src/Umbraco.Core/Models/Membership/UserProfile.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-using System.Linq;
-using System.Reflection;
-using System.Runtime.Serialization;
-
-namespace Umbraco.Core.Models.Membership
-{
- ///
- /// Represents the Profile implementation for a backoffice User
- ///
- ///
- /// Should be internal until a proper user/membership implementation
- /// is part of the roadmap.
- ///
- [Serializable]
- [DataContract(IsReference = true)]
- internal class UserProfile : Profile, IUserProfile
- {
- public UserProfile()
- {
- SessionTimeout = 60;
- _sectionCollection = new ObservableCollection();
- _addedSections = new List();
- _removedSections = new List();
- _sectionCollection.CollectionChanged += SectionCollectionChanged;
- }
-
- private readonly List _addedSections;
- private readonly List _removedSections;
- private readonly ObservableCollection _sectionCollection;
- private int _sessionTimeout;
- private int _startContentId;
- private int _startMediaId;
-
- private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo(x => x.SessionTimeout);
- private static readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartContentId);
- private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartMediaId);
- private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedSections);
-
- ///
- /// Gets or sets the session timeout.
- ///
- ///
- /// The session timeout.
- ///
- [DataMember]
- public int SessionTimeout
- {
- get
- {
- return _sessionTimeout;
- }
- set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _sessionTimeout = value;
- return _sessionTimeout;
- }, _sessionTimeout, SessionTimeoutSelector);
- }
- }
-
- ///
- /// Gets or sets the start content id.
- ///
- ///
- /// The start content id.
- ///
- [DataMember]
- public int StartContentId
- {
- get
- {
- return _startContentId;
- }
- set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _startContentId = value;
- return _startContentId;
- }, _startContentId, StartContentIdSelector);
- }
- }
-
- ///
- /// Gets or sets the start media id.
- ///
- ///
- /// The start media id.
- ///
- [DataMember]
- public int StartMediaId
- {
- get
- {
- return _startMediaId;
- }
- set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _startMediaId = value;
- return _startMediaId;
- }, _startMediaId, StartMediaIdSelector);
- }
- }
-
- public IEnumerable AllowedSections
- {
- get { return _sectionCollection; }
- }
-
- public void RemoveAllowedSection(string sectionAlias)
- {
- _sectionCollection.Remove(sectionAlias);
- }
-
- public void AddAllowedSection(string sectionAlias)
- {
- if (!_sectionCollection.Contains(sectionAlias))
- {
- _sectionCollection.Add(sectionAlias);
- }
- }
-
- ///
- /// Whenever resetting occurs, clear the remembered add/removed collections, even if
- /// rememberPreviouslyChangedProperties is true, the AllowedSections property will still
- /// be flagged as dirty.
- ///
- ///
- public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
- {
- _addedSections.Clear();
- _removedSections.Clear();
- base.ResetDirtyProperties(rememberPreviouslyChangedProperties);
- }
-
- ///
- /// Used internally to check if we need to add a section in the repository to the db
- ///
- internal IEnumerable AddedSections
- {
- get { return _addedSections; }
- }
-
- ///
- /// Used internally to check if we need to remove a section in the repository to the db
- ///
- internal IEnumerable RemovedSections
- {
- get { return _removedSections; }
- }
-
- ///
- /// Handles the collection changed event in order for us to flag the AllowedSections property as changed
- ///
- ///
- ///
- void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
- {
- OnPropertyChanged(AllowedSectionsSelector);
-
- if (e.Action == NotifyCollectionChangedAction.Add)
- {
- //remove from the removed/added sections (since people could add/remove all they want in one request)
- _removedSections.RemoveAll(s => s == e.NewItems.Cast().First());
- _addedSections.RemoveAll(s => s == e.NewItems.Cast().First());
-
- //add to the added sections
- _addedSections.Add(e.NewItems.Cast().First());
- }
- else if (e.Action == NotifyCollectionChangedAction.Remove)
- {
- //remove from the removed/added sections (since people could add/remove all they want in one request)
- _removedSections.RemoveAll(s => s == e.OldItems.Cast().First());
- _addedSections.RemoveAll(s => s == e.OldItems.Cast().First());
-
- //add to the added sections
- _removedSections.Add(e.OldItems.Cast().First());
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Membership/UserType.cs b/src/Umbraco.Core/Models/Membership/UserType.cs
index b09a568c22..b5553c10d2 100644
--- a/src/Umbraco.Core/Models/Membership/UserType.cs
+++ b/src/Umbraco.Core/Models/Membership/UserType.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Mappers;
@@ -22,7 +23,13 @@ namespace Umbraco.Core.Models.Membership
[DataMember]
public string Name { get; set; }
+ ///
+ /// The set of default permissions for the user type
+ ///
+ ///
+ /// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
+ ///
[DataMember]
- public string Permissions { get; set; }
+ public IEnumerable Permissions { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Notification.cs b/src/Umbraco.Core/Models/Notification.cs
new file mode 100644
index 0000000000..e5d3236b5e
--- /dev/null
+++ b/src/Umbraco.Core/Models/Notification.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Umbraco.Core.Models
+{
+ internal class Notification
+ {
+ public Notification(int entityId, int userId, string action, Guid entityType)
+ {
+ EntityId = entityId;
+ UserId = userId;
+ Action = action;
+ EntityType = entityType;
+ }
+
+ public int EntityId { get; private set; }
+ public int UserId { get; private set; }
+ public string Action { get; private set; }
+ public Guid EntityType { get; private set; }
+ }
+}
diff --git a/src/Umbraco.Core/Models/PreValue.cs b/src/Umbraco.Core/Models/PreValue.cs
index 05b7a5f5df..a9e539a960 100644
--- a/src/Umbraco.Core/Models/PreValue.cs
+++ b/src/Umbraco.Core/Models/PreValue.cs
@@ -5,10 +5,17 @@
///
public class PreValue
{
+ public PreValue(int id, string value, int sortOrder)
+ {
+ Id = id;
+ Value = value;
+ SortOrder = sortOrder;
+ }
+
public PreValue(int id, string value)
{
- Value = value;
- Id = id;
+ Id = id;
+ Value = value;
}
public PreValue(string value)
@@ -25,5 +32,10 @@
/// The database id for the pre-value field value
///
public int Id { get; private set; }
+
+ ///
+ /// The sort order stored for the pre-value field value
+ ///
+ public int SortOrder { get; private set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index 3b2801aa24..1721efe5de 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -44,7 +44,7 @@ namespace Umbraco.Core.Models
private static readonly PropertyInfo ValueSelector = ExpressionHelper.GetPropertyInfo(x => x.Value);
private static readonly PropertyInfo VersionSelector = ExpressionHelper.GetPropertyInfo(x => x.Version);
-
+
///
/// Returns the instance of the tag support, by default tags are not enabled
///
@@ -68,9 +68,14 @@ namespace Umbraco.Core.Models
///
/// Returns the DatabaseType that the underlaying DataType is using to store its values
///
- /// Only used internally when saving the property value
+ ///
+ /// Only used internally when saving the property value.
+ ///
[IgnoreDataMember]
- internal DataTypeDatabaseType DataTypeDatabaseType { get { return _propertyType.DataTypeDatabaseType; } }
+ internal DataTypeDatabaseType DataTypeDatabaseType
+ {
+ get { return _propertyType.DataTypeDatabaseType; }
+ }
///
/// Returns the PropertyType, which this Property is based on
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index c1526a9ffb..b275a6912f 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -15,6 +15,7 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class PropertyType : Entity, IEquatable
{
+ private readonly bool _isExplicitDbType;
private string _name;
private string _alias;
private string _description;
@@ -30,16 +31,29 @@ namespace Umbraco.Core.Models
public PropertyType(IDataTypeDefinition dataTypeDefinition)
{
if(dataTypeDefinition.HasIdentity)
- DataTypeDefinitionId = dataTypeDefinition.Id;
+ _dataTypeDefinitionId = dataTypeDefinition.Id;
- PropertyEditorAlias = dataTypeDefinition.PropertyEditorAlias;
- DataTypeDatabaseType = dataTypeDefinition.DatabaseType;
+ _propertyEditorAlias = dataTypeDefinition.PropertyEditorAlias;
+ _dataTypeDatabaseType = dataTypeDefinition.DatabaseType;
}
internal PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType)
+ : this(propertyEditorAlias, dataTypeDatabaseType, false)
{
PropertyEditorAlias = propertyEditorAlias;
- DataTypeDatabaseType = dataTypeDatabaseType;
+ }
+
+ ///
+ /// Used internally to assign an explicity database type for this property type regardless of what the underlying data type/property editor is.
+ ///
+ ///
+ ///
+ ///
+ internal PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType, bool isExplicitDbType)
+ {
+ _isExplicitDbType = isExplicitDbType;
+ _propertyEditorAlias = propertyEditorAlias;
+ _dataTypeDatabaseType = dataTypeDatabaseType;
}
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
@@ -165,6 +179,9 @@ namespace Umbraco.Core.Models
get { return _dataTypeDatabaseType; }
set
{
+ //don't allow setting this if an explicit declaration has been made in the ctor
+ if (_isExplicitDbType) return;
+
SetPropertyValueAndDetectChanges(o =>
{
_dataTypeDatabaseType = value;
@@ -377,8 +394,16 @@ namespace Umbraco.Core.Models
//Check against Regular Expression for Legacy DataTypes - Validation exists and value is not null:
if(string.IsNullOrEmpty(ValidationRegExp) == false && (value != null && string.IsNullOrEmpty(value.ToString()) == false))
{
- var regexPattern = new Regex(ValidationRegExp);
- return regexPattern.IsMatch(value.ToString());
+ try
+ {
+ var regexPattern = new Regex(ValidationRegExp);
+ return regexPattern.IsMatch(value.ToString());
+ }
+ catch
+ {
+ throw new Exception(string .Format("Invalid validation expression on property {0}",this.Alias));
+ }
+
}
//TODO Add PropertyEditor validation when its relevant to introduce
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs
index 5a3c79ebc1..676eb05ef0 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs
@@ -141,9 +141,21 @@ namespace Umbraco.Core.Models.PublishedContent
if (GetPublishedContentTypeCallback != null)
return GetPublishedContentTypeCallback(alias);
- var contentType = itemType == PublishedItemType.Content
- ? (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias)
- : (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias);
+ IContentTypeComposition contentType;
+ switch (itemType)
+ {
+ case PublishedItemType.Content:
+ contentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias);
+ break;
+ case PublishedItemType.Media:
+ contentType = ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias);
+ break;
+ case PublishedItemType.Member:
+ contentType = ApplicationContext.Current.Services.MemberTypeService.Get(alias);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("itemType");
+ }
if (contentType == null)
throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
index 295cbdef66..8ef077a968 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
@@ -80,11 +80,8 @@ namespace Umbraco.Core.Models.PublishedContent
private void InitializeConverters()
{
- var converters = PropertyValueConvertersResolver.Current.Converters.ToArray();
-
- var defaultConverters = converters
- .Where(x => x.GetType().GetCustomAttribute(false) != null)
- .ToArray();
+ var converters = PropertyValueConvertersResolver.Current.Converters.ToArray();
+ var defaultConvertersWithAttributes = PropertyValueConvertersResolver.Current.DefaultConverters;
_converter = null;
@@ -98,8 +95,14 @@ namespace Umbraco.Core.Models.PublishedContent
else if (foundConverters.Length > 1)
{
//more than one was found, we need to first figure out if one of these is an Umbraco default value type converter
- var nonDefault = foundConverters.Except(defaultConverters).ToArray();
- if (nonDefault.Length > 1)
+ //get the non-default and see if we have one
+ var nonDefault = foundConverters.Except(defaultConvertersWithAttributes.Select(x => x.Item1)).ToArray();
+ if (nonDefault.Length == 1)
+ {
+ //there's only 1 custom converter registered that so use it
+ _converter = nonDefault[0];
+ }
+ else if (nonDefault.Length > 1)
{
//this is not allowed, there cannot be more than 1 custom converter
throw new InvalidOperationException(
@@ -109,9 +112,31 @@ namespace Umbraco.Core.Models.PublishedContent
ContentType.Alias, PropertyTypeAlias,
nonDefault[1].GetType().FullName, nonDefault[0].GetType().FullName));
}
+ else
+ {
+ //we need to remove any converters that have been shadowed by another converter
+ var foundDefaultConvertersWithAttributes = defaultConvertersWithAttributes.Where(x => foundConverters.Contains(x.Item1));
+ var shadowedTypes = foundDefaultConvertersWithAttributes.SelectMany(x => x.Item2.DefaultConvertersToShadow);
+ var shadowedDefaultConverters = foundConverters.Where(x => shadowedTypes.Contains(x.GetType()));
+ var nonShadowedDefaultConverters = foundConverters.Except(shadowedDefaultConverters).ToArray();
- //there's only 1 custom converter registered that so use it
- _converter = nonDefault[0];
+ if (nonShadowedDefaultConverters.Length == 1)
+ {
+ //assign to the single default converter
+ _converter = nonShadowedDefaultConverters[0];
+ }
+ else if (nonShadowedDefaultConverters.Length > 1)
+ {
+ //this is not allowed, there cannot be more than 1 custom converter
+ throw new InvalidOperationException(
+ string.Format("Type '{2}' cannot be an IPropertyValueConverter"
+ + " for property '{1}' of content type '{0}' because type '{3}' has already been detected as a converter"
+ + " for that property, and only one converter can exist for a property.",
+ ContentType.Alias, PropertyTypeAlias,
+ nonShadowedDefaultConverters[1].GetType().FullName, nonShadowedDefaultConverters[0].GetType().FullName));
+ }
+ }
+
}
// get the cache levels, quietely fixing the inconsistencies (no need to throw, really)
@@ -244,7 +269,7 @@ namespace Umbraco.Core.Models.PublishedContent
: Enumerable.Empty();
}
- class CompatConverter : PropertyValueConverterBase
+ private class CompatConverter : PropertyValueConverterBase
{
private readonly IPropertyEditorValueConverter _converter;
diff --git a/src/Umbraco.Core/Models/PublishedItemType.cs b/src/Umbraco.Core/Models/PublishedItemType.cs
index fd0f218cce..677205f61a 100644
--- a/src/Umbraco.Core/Models/PublishedItemType.cs
+++ b/src/Umbraco.Core/Models/PublishedItemType.cs
@@ -13,6 +13,11 @@ namespace Umbraco.Core.Models
///
/// A media.
///
- Media
+ Media,
+
+ ///
+ /// A member.
+ ///
+ Member
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/PreviewXmlDto.cs b/src/Umbraco.Core/Models/Rdbms/PreviewXmlDto.cs
index f0f3ebffd0..d981855f24 100644
--- a/src/Umbraco.Core/Models/Rdbms/PreviewXmlDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PreviewXmlDto.cs
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Models.Rdbms
internal class PreviewXmlDto
{
[Column("nodeId")]
- [PrimaryKeyColumn(AutoIncrement = false, Name = "", OnColumns = "nodeId, versionId")]
+ [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsContentPreviewXml", OnColumns = "nodeId, versionId")]
[ForeignKey(typeof(ContentDto), Column = "nodeId")]
public int NodeId { get; set; }
diff --git a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs
index e9c79ae57e..9833d9ab74 100644
--- a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs
@@ -30,7 +30,6 @@ namespace Umbraco.Core.Models.Rdbms
public DateTime DateRegistered { get; set; }
[Column("lastNotifiedDate")]
- [Constraint(Default = "getdate()")]
public DateTime LastNotified { get; set; }
[Column("isActive")]
diff --git a/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs b/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs
index 3315d1f7c1..6826377856 100644
--- a/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.Rdbms
internal class UserLoginDto
{
[Column("contextID")]
- [Index(IndexTypes.Clustered, Name = "umbracoUserLogins_Index")]
+ [Index(IndexTypes.Clustered, Name = "IX_umbracoUserLogins_Index")]
public Guid ContextId { get; set; }
[Column("userID")]
diff --git a/src/Umbraco.Core/Models/RelationType.cs b/src/Umbraco.Core/Models/RelationType.cs
index e1f5b4a388..ae20482229 100644
--- a/src/Umbraco.Core/Models/RelationType.cs
+++ b/src/Umbraco.Core/Models/RelationType.cs
@@ -21,9 +21,18 @@ namespace Umbraco.Core.Models
public RelationType(Guid childObjectType, Guid parentObjectType, string @alias)
{
+ Mandate.ParameterNotNullOrEmpty(@alias, "alias");
_childObjectType = childObjectType;
_parentObjectType = parentObjectType;
_alias = alias;
+ Name = _alias;
+ }
+
+ public RelationType(Guid childObjectType, Guid parentObjectType, string @alias, string name)
+ :this(childObjectType, parentObjectType, @alias)
+ {
+ Mandate.ParameterNotNullOrEmpty(name, "name");
+ Name = name;
}
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
diff --git a/src/Umbraco.Core/Models/Template.cs b/src/Umbraco.Core/Models/Template.cs
index 33bc3e6b86..387a345039 100644
--- a/src/Umbraco.Core/Models/Template.cs
+++ b/src/Umbraco.Core/Models/Template.cs
@@ -183,5 +183,11 @@ namespace Umbraco.Core.Models
if (Key == Guid.Empty)
Key = Guid.NewGuid();
}
+
+
+ public void SetMasterTemplate(ITemplate masterTemplate)
+ {
+ MasterTemplateId = new Lazy(() => masterTemplate.Id);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs
index b735f385d8..24261b4989 100644
--- a/src/Umbraco.Core/Models/UmbracoEntity.cs
+++ b/src/Umbraco.Core/Models/UmbracoEntity.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Reflection;
using Umbraco.Core.Models.EntityBase;
@@ -53,6 +54,13 @@ namespace Umbraco.Core.Models
Trashed = trashed;
}
+ // for MySql
+ public UmbracoEntity(UInt64 trashed)
+ {
+ AdditionalData = new Dictionary();
+ Trashed = trashed == 1;
+ }
+
public int CreatorId
{
get { return _creatorId; }
diff --git a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs
new file mode 100644
index 0000000000..edcc25ade8
--- /dev/null
+++ b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web.UI.WebControls;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Models
+{
+ internal static class UmbracoEntityExtensions
+ {
+
+ public static object GetAdditionalDataValueIgnoreCase(this IUmbracoEntity entity, string key, object defaultVal)
+ {
+ if (entity.AdditionalData.ContainsKeyIgnoreCase(key) == false) return defaultVal;
+ return entity.AdditionalData.GetValueIgnoreCase(key, defaultVal);
+ }
+
+ public static bool IsContainer(this IUmbracoEntity entity)
+ {
+ if (entity.AdditionalData.ContainsKeyIgnoreCase("IsContainer") == false) return false;
+ var val = entity.AdditionalData.GetValueIgnoreCase("IsContainer", null);
+ if (val is bool && (bool) val)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ }
+}
diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs
index 603c508e0f..90d173b49a 100644
--- a/src/Umbraco.Core/ObjectExtensions.cs
+++ b/src/Umbraco.Core/ObjectExtensions.cs
@@ -246,12 +246,14 @@ namespace Umbraco.Core
else if (destinationType == typeof(Double))
{
Double value;
- return Double.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ var input2 = NormalizeNumberDecimalSeparator(input);
+ return Double.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail();
}
else if (destinationType == typeof(Single))
{
Single value;
- return Single.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ var input2 = NormalizeNumberDecimalSeparator(input);
+ return Single.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail();
}
else if (destinationType == typeof(Char))
{
@@ -292,7 +294,20 @@ namespace Umbraco.Core
else if (destinationType == typeof(DateTime))
{
DateTime value;
- return DateTime.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ if (DateTime.TryParse(input, out value))
+ {
+ switch (value.Kind)
+ {
+ case DateTimeKind.Unspecified:
+ case DateTimeKind.Utc:
+ return Attempt.Succeed(value);
+ case DateTimeKind.Local:
+ return Attempt.Succeed(value.ToUniversalTime());
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ return Attempt.Fail();
}
else if (destinationType == typeof(DateTimeOffset))
{
@@ -307,7 +322,8 @@ namespace Umbraco.Core
else if (destinationType == typeof(Decimal))
{
Decimal value;
- return Decimal.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ var input2 = NormalizeNumberDecimalSeparator(input);
+ return Decimal.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail();
}
else if (destinationType == typeof(Version))
{
@@ -319,6 +335,14 @@ namespace Umbraco.Core
return null; // we can't decide...
}
+ private readonly static char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','};
+
+ private static string NormalizeNumberDecimalSeparator(string s)
+ {
+ var normalized = System.Threading.Thread.CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator[0];
+ return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized);
+ }
+
internal static void CheckThrowObjectDisposed(this IDisposable disposable, bool isDisposed, string objectname)
{
//TODO: Localise this exception
@@ -526,7 +550,7 @@ namespace Umbraco.Core
if (type == typeof(bool)) return XmlConvert.ToString((bool)value);
if (type == typeof(byte)) return XmlConvert.ToString((byte)value);
if (type == typeof(char)) return XmlConvert.ToString((char)value);
- if (type == typeof(DateTime)) return XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.RoundtripKind);
+ if (type == typeof(DateTime)) return XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.Unspecified);
if (type == typeof(DateTimeOffset)) return XmlConvert.ToString((DateTimeOffset)value);
if (type == typeof(decimal)) return XmlConvert.ToString((decimal)value);
if (type == typeof(double)) return XmlConvert.ToString((double)value);
diff --git a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs
index 875fcd051f..a31598bfc8 100644
--- a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs
+++ b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
using umbraco.interfaces;
namespace Umbraco.Core.ObjectResolution
@@ -8,7 +9,10 @@ namespace Umbraco.Core.ObjectResolution
///
/// A resolver to return all IApplicationEvents objects
///
- internal sealed class ApplicationEventsResolver : ManyObjectsResolverBase
+ ///
+ /// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances.
+ ///
+ internal sealed class ApplicationEventsResolver : ManyObjectsResolverBase, IDisposable
{
private readonly LegacyStartupHandlerResolver _legacyResolver;
@@ -53,7 +57,7 @@ namespace Umbraco.Core.ObjectResolution
protected override bool SupportsClear
{
- get { return false; }
+ get { return false; }
}
protected override bool SupportsInsert
@@ -61,7 +65,7 @@ namespace Umbraco.Core.ObjectResolution
get { return false; }
}
- private class LegacyStartupHandlerResolver : ManyObjectsResolverBase
+ private class LegacyStartupHandlerResolver : ManyObjectsResolverBase, IDisposable
{
internal LegacyStartupHandlerResolver(IEnumerable legacyStartupHandlers)
: base(legacyStartupHandlers)
@@ -73,7 +77,72 @@ namespace Umbraco.Core.ObjectResolution
{
get { return Values; }
}
- }
+ public void Dispose()
+ {
+ ResetCollections();
+ }
+ }
+
+ private bool _disposed;
+ private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
+
+ ///
+ /// Gets a value indicating whether this instance is disposed.
+ ///
+ ///
+ /// true if this instance is disposed; otherwise, false.
+ ///
+ public bool IsDisposed
+ {
+ get { return _disposed; }
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ /// 2
+ public void Dispose()
+ {
+ Dispose(true);
+
+ // Use SupressFinalize in case a subclass of this type implements a finalizer.
+ GC.SuppressFinalize(this);
+ }
+
+ ~ApplicationEventsResolver()
+ {
+ // Run dispose but let the class know it was due to the finalizer running.
+ Dispose(false);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ // Only operate if we haven't already disposed
+ if (IsDisposed || disposing == false) return;
+
+ using (new WriteLock(_disposalLocker))
+ {
+ // Check again now we're inside the lock
+ if (IsDisposed) return;
+
+ // Call to actually release resources. This method is only
+ // kept separate so that the entire disposal logic can be used as a VS snippet
+ DisposeResources();
+
+ // Indicate that the instance has been disposed.
+ _disposed = true;
+ }
+ }
+
+ ///
+ /// Clear out all of the instances, we don't want them hanging around and cluttering up memory
+ ///
+ private void DisposeResources()
+ {
+ _legacyResolver.Dispose();
+ ResetCollections();
+ }
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
index 6aaf59106c..016a9c8f11 100644
--- a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
+++ b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
@@ -18,7 +18,7 @@ namespace Umbraco.Core.ObjectResolution
/// Important notes about this resolver: it does not support Insert or Remove and therefore does not support any ordering unless
/// the types are marked with the WeightedPluginAttribute.
///
- internal abstract class LazyManyObjectsResolverBase : ManyObjectsResolverBase
+ public abstract class LazyManyObjectsResolverBase : ManyObjectsResolverBase
where TResolved : class
where TResolver : ResolverBase
{
diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs
index 0edaf68243..8bb5995891 100644
--- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs
+++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs
@@ -322,7 +322,7 @@ namespace Umbraco.Core.ObjectResolution
}
///
- /// Clears the list of types.
+ /// Clears the list of types
///
/// the resolver does not support clearing types.
public virtual void Clear()
@@ -336,6 +336,20 @@ namespace Umbraco.Core.ObjectResolution
}
}
+ ///
+ /// WARNING! Do not use this unless you know what you are doing, clear all types registered and instances
+ /// created. Typically only used if a resolver is no longer used in an application and memory is to be GC'd
+ ///
+ internal void ResetCollections()
+ {
+ using (new WriteLock(_lock))
+ {
+ _instanceTypes.Clear();
+ _sortedValues = null;
+ _applicationInstances = null;
+ }
+ }
+
///
/// Inserts a type at the specified index.
///
diff --git a/src/Umbraco.Core/Packaging/Models/InstallAction.cs b/src/Umbraco.Core/Packaging/Models/InstallAction.cs
new file mode 100644
index 0000000000..257f0c5a03
--- /dev/null
+++ b/src/Umbraco.Core/Packaging/Models/InstallAction.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Runtime.Serialization;
+using System.Xml;
+
+namespace Umbraco.Core.Packaging.Models
+{
+ [Serializable]
+ [DataContract(IsReference = true)]
+ internal class InstallAction
+ {
+ public string Alias { get; set; }
+
+ public string PackageName { get; set; }
+
+ public string RunAt { get; set; }//NOTE Should this default to "install"
+
+ public bool Undo { get; set; }
+
+ public XmlNode XmlData { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Packaging/Models/InstallationSummary.cs b/src/Umbraco.Core/Packaging/Models/InstallationSummary.cs
new file mode 100644
index 0000000000..7ee077855d
--- /dev/null
+++ b/src/Umbraco.Core/Packaging/Models/InstallationSummary.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Umbraco.Core.Packaging.Models
+{
+ [Serializable]
+ [DataContract(IsReference = true)]
+ internal class InstallationSummary
+ {
+ public MetaData MetaData { get; set; }
+ public IEnumerable DataTypesInstalled { get; set; }
+ public IEnumerable LanguagesInstalled { get; set; }
+ public IEnumerable DictionaryItemsInstalled { get; set; }
+ public IEnumerable MacrosInstalled { get; set; }
+ public IEnumerable> FilesInstalled { get; set;}
+ public IEnumerable TemplatesInstalled { get; set; }
+ public IEnumerable DocumentTypesInstalled { get; set; }
+ public IEnumerable StylesheetsInstalled { get; set; }
+ public IEnumerable DocumentsInstalled { get; set; }
+ public IEnumerable InstallActions { get; set; }
+ public IEnumerable UninstallActions { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Packaging/Models/MetaData.cs b/src/Umbraco.Core/Packaging/Models/MetaData.cs
new file mode 100644
index 0000000000..28f1af230e
--- /dev/null
+++ b/src/Umbraco.Core/Packaging/Models/MetaData.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace Umbraco.Core.Packaging.Models
+{
+ [Serializable]
+ [DataContract(IsReference = true)]
+ internal class MetaData
+ {
+ public string Name { get; set; }
+ public string Version { get; set; }
+ public string Url { get; set; }
+ public string License { get; set; }
+ public string LicenseUrl { get; set; }
+ public int ReqMajor { get; set; }
+ public int ReqMinor { get; set; }
+ public int ReqPatch { get; set; }
+ public string AuthorName { get; set; }
+ public string AuthorUrl { get; set; }
+ public string Readme { get; set; }
+ public string Control { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Packaging/Models/UninstallAction.cs b/src/Umbraco.Core/Packaging/Models/UninstallAction.cs
new file mode 100644
index 0000000000..886c10afba
--- /dev/null
+++ b/src/Umbraco.Core/Packaging/Models/UninstallAction.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Runtime.Serialization;
+using System.Xml;
+
+namespace Umbraco.Core.Packaging.Models
+{
+ [Serializable]
+ [DataContract(IsReference = true)]
+ internal class UninstallAction
+ {
+ public string Alias { get; set; }
+
+ public string PackageName { get; set; }
+
+ public string RunAt { get; set; }//NOTE Should this default to "install"
+
+ public bool Undo { get; set; }//NOTE: Should thid default to "False"?
+
+ public XmlNode XmlData { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Packaging/PackageBuilding.cs b/src/Umbraco.Core/Packaging/PackageBuilding.cs
new file mode 100644
index 0000000000..6cbb17b158
--- /dev/null
+++ b/src/Umbraco.Core/Packaging/PackageBuilding.cs
@@ -0,0 +1,18 @@
+using Umbraco.Core.Services;
+
+namespace Umbraco.Core.Packaging
+{
+ internal interface IPackageBuilding
+ {
+ }
+
+ internal class PackageBuilding : IPackageBuilding
+ {
+ private readonly PackagingService _packagingService;
+
+ public PackageBuilding(PackagingService packagingService)
+ {
+ _packagingService = packagingService;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Packaging/PackageExtraction.cs b/src/Umbraco.Core/Packaging/PackageExtraction.cs
new file mode 100644
index 0000000000..d3de2a3da4
--- /dev/null
+++ b/src/Umbraco.Core/Packaging/PackageExtraction.cs
@@ -0,0 +1,34 @@
+using System;
+using System.IO;
+using Umbraco.Core.IO;
+
+namespace Umbraco.Core.Packaging
+{
+ internal interface IPackageExtraction
+ {
+ bool Extract(string packageFilePath, string destinationFolder);
+ string ExtractToTemporaryFolder(string packageFilePath);
+ string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml");
+ }
+
+ internal class PackageExtraction : IPackageExtraction
+ {
+ public bool Extract(string packageFilePath, string destinationFolder)
+ {
+ return true;
+ }
+
+ public string ExtractToTemporaryFolder(string packageFilePath)
+ {
+ string tempDir = Path.Combine(IOHelper.MapPath(SystemDirectories.Data), Guid.NewGuid().ToString("D"));
+ Directory.CreateDirectory(tempDir);
+ Extract(packageFilePath, tempDir);
+ return tempDir;
+ }
+
+ public string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml")
+ {
+ return string.Empty;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs
new file mode 100644
index 0000000000..5d8c292098
--- /dev/null
+++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs
@@ -0,0 +1,35 @@
+using Umbraco.Core.Packaging.Models;
+using Umbraco.Core.Services;
+
+namespace Umbraco.Core.Packaging
+{
+ internal interface IPackageInstallation
+ {
+ InstallationSummary InstallPackage(string packageFilePath, int userId = 0);
+ MetaData GetPackageMetaData(string packageFilePath);
+ }
+
+ internal class PackageInstallation : IPackageInstallation
+ {
+ private readonly PackagingService _packagingService;
+ private readonly PackageExtraction _packageExtraction;
+
+ public PackageInstallation(PackagingService packagingService, PackageExtraction packageExtraction)
+ {
+ _packagingService = packagingService;
+ _packageExtraction = packageExtraction;
+ }
+
+ public InstallationSummary InstallPackage(string packageFilePath, int userId = 0)
+ {
+ var summary = new InstallationSummary();
+ return summary;
+ }
+
+ public MetaData GetPackageMetaData(string packageFilePath)
+ {
+ var metaData = new MetaData();
+ return metaData;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs
index 60e176096f..273d605bef 100644
--- a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs
+++ b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs
@@ -151,6 +151,21 @@ namespace Umbraco.Core.Persistence.Caching
_keyTracker.Remove(key);
}
+ public void Delete(Type type, int entityId)
+ {
+ var key = GetCompositeId(type, entityId);
+ if (_memoryCache != null)
+ {
+ _memoryCache.Remove(key);
+ }
+ else
+ {
+ HttpRuntime.Cache.Remove(key);
+ }
+
+ _keyTracker.Remove(key);
+ }
+
///
/// Clear cache by type
///
diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs
new file mode 100644
index 0000000000..e495d61550
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs
@@ -0,0 +1,13 @@
+namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
+{
+ ///
+ /// Represents a database index definition retreived by querying the database
+ ///
+ internal class DbIndexDefinition
+ {
+ public virtual string IndexName { get; set; }
+ public virtual string TableName { get; set; }
+ public virtual string ColumnName { get; set; }
+ public virtual bool IsUnique { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
new file mode 100644
index 0000000000..f7bd9e31c1
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Data.Common;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Core.Persistence
+{
+ internal static class DbConnectionExtensions
+ {
+
+ public static DatabaseProviders DetectProviderFromConnectionString(string connString)
+ {
+ var builder = new DbConnectionStringBuilder {ConnectionString = connString};
+ var allKeys = builder.Keys.Cast();
+
+ var mySql = new[] {"Server", "Database", "Uid", "Pwd"};
+ if (mySql.All(x => allKeys.InvariantContains(x)))
+ {
+ return DatabaseProviders.MySql;
+ }
+
+ if (allKeys.InvariantContains("Data Source")
+ //this dictionary is case insensitive
+ && builder["Data source"].ToString().InvariantContains(".sdf"))
+ {
+ return DatabaseProviders.SqlServerCE;
+ }
+
+ return DatabaseProviders.SqlServer;
+ }
+
+ public static bool IsConnectionAvailable(string connString, DatabaseProviders provider)
+ {
+ DbProviderFactory factory;
+ switch (provider)
+ {
+ case DatabaseProviders.SqlServer:
+ case DatabaseProviders.SqlAzure:
+ factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
+ break;
+ case DatabaseProviders.SqlServerCE:
+ factory = DbProviderFactories.GetFactory("System.Data.SqlServerCe.4.0");
+ break;
+ case DatabaseProviders.MySql:
+ factory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient");
+ break;
+ case DatabaseProviders.PostgreSQL:
+ case DatabaseProviders.Oracle:
+ case DatabaseProviders.SQLite:
+ default:
+ throw new NotSupportedException("The provider " + provider + " is not supported");
+ }
+
+ var conn = factory.CreateConnection();
+ if (conn == null)
+ {
+ throw new InvalidOperationException("Could not create a connection for provider " + provider);
+ }
+ conn.ConnectionString = connString;
+ using (var connection = conn)
+ {
+ return connection.IsAvailable();
+ }
+ }
+
+ public static bool IsAvailable(this IDbConnection connection)
+ {
+ try
+ {
+ connection.Open();
+ connection.Close();
+ }
+ catch (SqlException)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/EntityNotFoundException.cs b/src/Umbraco.Core/Persistence/EntityNotFoundException.cs
new file mode 100644
index 0000000000..1cc66a2ba2
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/EntityNotFoundException.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Core.Persistence
+{
+
+ //TODO: Would be good to use this exception type anytime we cannot find an entity
+
+ ///
+ /// An exception used to indicate that an umbraco entity could not be found
+ ///
+ public class EntityNotFoundException : Exception
+ {
+ public object Id { get; private set; }
+ private readonly string _msg;
+
+ public EntityNotFoundException(object id, string msg)
+ {
+ Id = id;
+ _msg = msg;
+ }
+
+ public EntityNotFoundException(string msg)
+ {
+ _msg = msg;
+ }
+
+ public override string Message
+ {
+ get { return _msg; }
+ }
+
+ public override string ToString()
+ {
+ var result = base.ToString();
+
+ if (Id != null)
+ {
+ return "Umbraco entity (id: " + Id + ") not found. " + result;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs
index 188a834b62..b64ceba9aa 100644
--- a/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs
@@ -30,7 +30,7 @@ namespace Umbraco.Core.Persistence.Factories
NodeId = entity.Id,
Email = entity.Email,
LoginName = entity.Username,
- Password = entity.Password,
+ Password = entity.RawPasswordValue,
ContentVersionDto = BuildDto(entity as Member)
};
return member;
diff --git a/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs
new file mode 100644
index 0000000000..207eea83b3
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs
@@ -0,0 +1,64 @@
+using System;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Factories
+{
+ internal class MemberGroupFactory : IEntityFactory
+ {
+
+ private readonly Guid _nodeObjectTypeId;
+
+ public MemberGroupFactory()
+ {
+ _nodeObjectTypeId = new Guid(Constants.ObjectTypes.MemberGroup);
+ }
+
+ #region Implementation of IEntityFactory
+
+ public IMemberGroup BuildEntity(NodeDto dto)
+ {
+ var template = new MemberGroup
+ {
+ CreateDate = dto.CreateDate,
+ Id = dto.NodeId,
+ Key = dto.UniqueId.Value,
+ Name = dto.Text
+ };
+
+ //on initial construction we don't want to have dirty properties tracked
+ // http://issues.umbraco.org/issue/U4-1946
+ template.ResetDirtyProperties(false);
+ return template;
+ }
+
+ public NodeDto BuildDto(IMemberGroup entity)
+ {
+ var dto = new NodeDto
+ {
+ CreateDate = entity.CreateDate,
+ NodeId = entity.Id,
+ Level = 0,
+ NodeObjectType = _nodeObjectTypeId,
+ ParentId = -1,
+ Path = "",
+ SortOrder = 0,
+ Text = entity.Name,
+ Trashed = false,
+ UniqueId = entity.Key,
+ UserId = entity.CreatorId
+ };
+
+ if (entity.HasIdentity)
+ {
+ dto.NodeId = entity.Id;
+ dto.Path = "-1," + entity.Id;
+ }
+
+ return dto;
+ }
+
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs
index 225b6173f5..7ff7c53b45 100644
--- a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs
@@ -19,13 +19,13 @@ namespace Umbraco.Core.Persistence.Factories
{
var properties = CreateProperties(_memberTypes[dto.ContentTypeAlias], dto.Properties, dto.CreateDate);
- var member = new Member(dto.Text, dto.Email, dto.LoginName, dto.Password, dto.ParentId, _memberTypes[dto.ContentTypeAlias])
+ var member = new Member(dto.Text, dto.Email, dto.LoginName, dto.Password, _memberTypes[dto.ContentTypeAlias])
{
Id = dto.NodeId,
CreateDate = dto.CreateDate,
UpdateDate = dto.UpdateDate,
Name = dto.Text,
- ProviderUserKey = dto.UniqueId,
+ ProviderUserKey = dto.NodeId,
Trashed = dto.Trashed,
Key = dto.UniqueId.Value,
CreatorId = dto.UserId.HasValue ? dto.UserId.Value : 0,
@@ -33,7 +33,6 @@ namespace Umbraco.Core.Persistence.Factories
Path = dto.Path,
SortOrder = dto.SortOrder,
Version = dto.VersionId,
- ContentTypeAlias = dto.ContentTypeAlias,
Properties = new PropertyCollection(properties)
};
diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
index 5caa8db61a..2a59f564e2 100644
--- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.Repositories;
namespace Umbraco.Core.Persistence.Factories
{
@@ -10,6 +11,8 @@ namespace Umbraco.Core.Persistence.Factories
{
public IMemberType BuildEntity(MemberTypeReadOnlyDto dto)
{
+ var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
+
var memberType = new MemberType(dto.ParentId)
{
Alias = dto.Alias,
@@ -31,12 +34,12 @@ namespace Umbraco.Core.Persistence.Factories
AllowedContentTypes = Enumerable.Empty()
};
- var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType);
+ var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType, standardPropertyTypes);
memberType.PropertyGroups = propertyTypeGroupCollection;
- var propertyTypes = GetPropertyTypes(dto, memberType);
- //By Convention we add 9 stnd PropertyTypes - This is only here to support loading of types that didn't have these conventions before.
- var standardPropertyTypes = Constants.Conventions.Member.StandardPropertyTypeStubs;
+ var propertyTypes = GetPropertyTypes(dto, memberType, standardPropertyTypes);
+
+ //By Convention we add 9 stnd PropertyTypes - This is only here to support loading of types that didn't have these conventions before.
foreach (var standardPropertyType in standardPropertyTypes)
{
if(dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue;
@@ -46,16 +49,17 @@ namespace Umbraco.Core.Persistence.Factories
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key,
- new Tuple(false, false, default(int)));
+ new MemberTypePropertyProfileAccess(false, false));
}
memberType.PropertyTypes = propertyTypes;
return memberType;
}
- private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType)
+ private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps)
{
- var propertyGroups = new PropertyGroupCollection();
+ var propertyGroups = new PropertyGroupCollection();
+
foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue))
{
var group = new PropertyGroup();
@@ -85,11 +89,23 @@ namespace Umbraco.Core.Persistence.Factories
{
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
- new Tuple(typeDto.CanEdit, typeDto.ViewOnProfile, typeDto.Id.Value));
+ new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit));
var tempGroupDto = groupDto;
- var propertyType = new PropertyType(typeDto.PropertyEditorAlias,
- typeDto.DbType.EnumParse(true))
+
+ //ensures that any built-in membership properties have their correct dbtype assigned no matter
+ //what the underlying data type is
+ var propDbType = MemberTypeRepository.GetDbTypeForBuiltInProperty(
+ typeDto.Alias,
+ typeDto.DbType.EnumParse(true),
+ standardProps);
+
+ var propertyType = new PropertyType(
+ typeDto.PropertyEditorAlias,
+ propDbType.Result,
+ //This flag tells the property type that it has an explicit dbtype and that it cannot be changed
+ // which is what we want for the built-in properties.
+ propDbType.Success)
{
Alias = typeDto.Alias,
DataTypeDefinitionId = typeDto.DataTypeId,
@@ -118,32 +134,47 @@ namespace Umbraco.Core.Persistence.Factories
return propertyGroups;
}
- private List GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType)
+
+
+ private List GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps)
{
//Find PropertyTypes that does not belong to a PropertyTypeGroup
var propertyTypes = new List();
- foreach (var propertyType in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue))
+ foreach (var typeDto in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue))
{
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
- memberType.MemberTypePropertyTypes.Add(propertyType.Alias,
- new Tuple(propertyType.CanEdit, propertyType.ViewOnProfile, propertyType.Id.Value));
- //PropertyType Collection
- propertyTypes.Add(new PropertyType(propertyType.PropertyEditorAlias,
- propertyType.DbType.EnumParse(true))
- {
- Alias = propertyType.Alias,
- DataTypeDefinitionId = propertyType.DataTypeId,
- Description = propertyType.Description,
- HelpText = propertyType.HelpText,
- Id = propertyType.Id.Value,
- Mandatory = propertyType.Mandatory,
- Name = propertyType.Name,
- SortOrder = propertyType.SortOrder,
- ValidationRegExp = propertyType.ValidationRegExp,
- PropertyGroupId = new Lazy(() => default(int)),
- CreateDate = dto.CreateDate,
- UpdateDate = dto.CreateDate
- });
+ memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
+ new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit));
+
+ //ensures that any built-in membership properties have their correct dbtype assigned no matter
+ //what the underlying data type is
+ var propDbType = MemberTypeRepository.GetDbTypeForBuiltInProperty(
+ typeDto.Alias,
+ typeDto.DbType.EnumParse(true),
+ standardProps);
+
+ var propertyType = new PropertyType(
+ typeDto.PropertyEditorAlias,
+ propDbType.Result,
+ //This flag tells the property type that it has an explicit dbtype and that it cannot be changed
+ // which is what we want for the built-in properties.
+ propDbType.Success)
+ {
+ Alias = typeDto.Alias,
+ DataTypeDefinitionId = typeDto.DataTypeId,
+ Description = typeDto.Description,
+ HelpText = typeDto.HelpText,
+ Id = typeDto.Id.Value,
+ Mandatory = typeDto.Mandatory,
+ Name = typeDto.Name,
+ SortOrder = typeDto.SortOrder,
+ ValidationRegExp = typeDto.ValidationRegExp,
+ PropertyGroupId = new Lazy(() => default(int)),
+ CreateDate = dto.CreateDate,
+ UpdateDate = dto.CreateDate
+ };
+
+ propertyTypes.Add(propertyType);
}
return propertyTypes;
}
diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
index adbb3ad7f5..a7afd2bafc 100644
--- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
@@ -11,17 +11,21 @@ namespace Umbraco.Core.Persistence.Factories
private readonly int _id;
private readonly DateTime _createDate;
private readonly DateTime _updateDate;
+ //a callback to create a property type which can be injected via a contructor
+ private readonly Func _propertyTypeCtor;
public PropertyGroupFactory(int id)
{
_id = id;
+ _propertyTypeCtor = (propertyEditorAlias, dbType, alias) => new PropertyType(propertyEditorAlias, dbType);
}
- public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate)
+ public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate, Func propertyTypeCtor)
{
_id = id;
_createDate = createDate;
_updateDate = updateDate;
+ _propertyTypeCtor = propertyTypeCtor;
}
#region Implementation of IEntityFactory,IEnumerable>
@@ -55,22 +59,23 @@ namespace Umbraco.Core.Persistence.Factories
foreach (var typeDto in typeDtos)
{
var tempGroupDto = groupDto;
- var propertyType = new PropertyType(typeDto.DataTypeDto.PropertyEditorAlias,
- typeDto.DataTypeDto.DbType.EnumParse(true))
- {
- Alias = typeDto.Alias,
- DataTypeDefinitionId = typeDto.DataTypeId,
- Description = typeDto.Description,
- Id = typeDto.Id,
- Name = typeDto.Name,
- HelpText = typeDto.HelpText,
- Mandatory = typeDto.Mandatory,
- SortOrder = typeDto.SortOrder,
- ValidationRegExp = typeDto.ValidationRegExp,
- PropertyGroupId = new Lazy(() => tempGroupDto.Id),
- CreateDate = _createDate,
- UpdateDate = _updateDate
- };
+ var propertyType = _propertyTypeCtor(typeDto.DataTypeDto.PropertyEditorAlias,
+ typeDto.DataTypeDto.DbType.EnumParse(true),
+ typeDto.Alias);
+
+ propertyType.Alias = typeDto.Alias;
+ propertyType.DataTypeDefinitionId = typeDto.DataTypeId;
+ propertyType.Description = typeDto.Description;
+ propertyType.Id = typeDto.Id;
+ propertyType.Name = typeDto.Name;
+ propertyType.HelpText = typeDto.HelpText;
+ propertyType.Mandatory = typeDto.Mandatory;
+ propertyType.SortOrder = typeDto.SortOrder;
+ propertyType.ValidationRegExp = typeDto.ValidationRegExp;
+ propertyType.PropertyGroupId = new Lazy(() => tempGroupDto.Id);
+ propertyType.CreateDate = _createDate;
+ propertyType.UpdateDate = _updateDate;
+
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
propertyType.ResetDirtyProperties(false);
diff --git a/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs b/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs
index 8aac081c33..f8fa4bed16 100644
--- a/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs
@@ -17,12 +17,12 @@ namespace Umbraco.Core.Persistence.Factories
public IRelation BuildEntity(RelationDto dto)
{
var entity = new Relation(dto.ParentId, dto.ChildId, _relationType)
- {
- Comment = dto.Comment,
- CreateDate = dto.Datetime,
- Id = dto.Id,
- UpdateDate = dto.Datetime
- };
+ {
+ Comment = dto.Comment,
+ CreateDate = dto.Datetime,
+ Id = dto.Id,
+ UpdateDate = dto.Datetime
+ };
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
entity.ResetDirtyProperties(false);
@@ -32,13 +32,13 @@ namespace Umbraco.Core.Persistence.Factories
public RelationDto BuildDto(IRelation entity)
{
var dto = new RelationDto
- {
- ChildId = entity.ChildId,
- Comment = string.IsNullOrEmpty(entity.Comment) ? string.Empty : entity.Comment,
- Datetime = entity.CreateDate,
- ParentId = entity.ParentId,
- RelationType = entity.RelationType.Id
- };
+ {
+ ChildId = entity.ChildId,
+ Comment = string.IsNullOrEmpty(entity.Comment) ? string.Empty : entity.Comment,
+ Datetime = entity.CreateDate,
+ ParentId = entity.ParentId,
+ RelationType = entity.RelationType.Id
+ };
if (entity.HasIdentity)
dto.Id = entity.Id;
diff --git a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs
index 81020fa593..08a80397ea 100644
--- a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs
@@ -10,11 +10,11 @@ namespace Umbraco.Core.Persistence.Factories
public IRelationType BuildEntity(RelationTypeDto dto)
{
var entity = new RelationType(dto.ChildObjectType, dto.ParentObjectType, dto.Alias)
- {
- Id = dto.Id,
- IsBidirectional = dto.Dual,
- Name = dto.Name
- };
+ {
+ Id = dto.Id,
+ IsBidirectional = dto.Dual,
+ Name = dto.Name
+ };
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
entity.ResetDirtyProperties(false);
@@ -24,14 +24,14 @@ namespace Umbraco.Core.Persistence.Factories
public RelationTypeDto BuildDto(IRelationType entity)
{
var dto = new RelationTypeDto
- {
- Alias = entity.Alias,
- ChildObjectType = entity.ChildObjectType,
- Dual = entity.IsBidirectional,
- Name = entity.Name,
- ParentObjectType = entity.ParentObjectType
- };
- if(entity.HasIdentity)
+ {
+ Alias = entity.Alias,
+ ChildObjectType = entity.ChildObjectType,
+ Dual = entity.IsBidirectional,
+ Name = entity.Name,
+ ParentObjectType = entity.ParentObjectType
+ };
+ if (entity.HasIdentity)
dto.Id = entity.Id;
return dto;
diff --git a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs
index 572bc7e624..51de2b2cab 100644
--- a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
@@ -18,24 +19,23 @@ namespace Umbraco.Core.Persistence.Factories
public IUser BuildEntity(UserDto dto)
{
+ var guidId = dto.Id.ToGuid();
var user = new User(_userType)
- {
- Id = dto.Id,
- ProfileId = dto.Id,
- StartContentId = dto.ContentStartId,
- StartMediaId = dto.MediaStartId.HasValue ? dto.MediaStartId.Value : -1,
- Password = dto.Password,
- Username = dto.Login,
- Name = dto.UserName,
- IsLockedOut = dto.Disabled,
- IsApproved = dto.Disabled == false,
- Email = dto.Email,
- Language = dto.UserLanguage,
- NoConsole = dto.NoConsole,
-
- //NOTE: The default permission come from the user type's default permissions
- DefaultPermissions = _userType.Permissions
- };
+ {
+ Id = dto.Id,
+ Key = guidId,
+ StartContentId = dto.ContentStartId,
+ StartMediaId = dto.MediaStartId.HasValue ? dto.MediaStartId.Value : -1,
+ RawPasswordValue = dto.Password,
+ Username = dto.Login,
+ Name = dto.UserName,
+ IsLockedOut = dto.NoConsole,
+ IsApproved = dto.Disabled == false,
+ Email = dto.Email,
+ Language = dto.UserLanguage,
+ //NOTE: The default permission come from the user type's default permissions
+ DefaultPermissions = _userType.Permissions
+ };
foreach (var app in dto.User2AppDtos)
{
@@ -58,8 +58,8 @@ namespace Umbraco.Core.Persistence.Factories
Disabled = entity.IsApproved == false,
Email = entity.Email,
Login = entity.Username,
- NoConsole = entity.NoConsole,
- Password = entity.Password,
+ NoConsole = entity.IsLockedOut,
+ Password = entity.RawPasswordValue,
UserLanguage = entity.Language,
UserName = entity.Name,
Type = short.Parse(entity.UserType.Id.ToString(CultureInfo.InvariantCulture)),
diff --git a/src/Umbraco.Core/Persistence/Factories/UserTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserTypeFactory.cs
index 66e79fd0cf..ad81d2b584 100644
--- a/src/Umbraco.Core/Persistence/Factories/UserTypeFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/UserTypeFactory.cs
@@ -1,4 +1,6 @@
-using Umbraco.Core.Models.Membership;
+using System.Globalization;
+using System.Linq;
+using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
@@ -10,12 +12,14 @@ namespace Umbraco.Core.Persistence.Factories
public IUserType BuildEntity(UserTypeDto dto)
{
var userType = new UserType
- {
- Alias = dto.Alias,
- Id = dto.Id,
- Name = dto.Name,
- Permissions = dto.DefaultPermissions
- };
+ {
+ Alias = dto.Alias,
+ Id = dto.Id,
+ Name = dto.Name,
+ Permissions = dto.DefaultPermissions.IsNullOrWhiteSpace()
+ ? Enumerable.Empty()
+ : dto.DefaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture))
+ };
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
userType.ResetDirtyProperties(false);
@@ -27,7 +31,7 @@ namespace Umbraco.Core.Persistence.Factories
var userType = new UserTypeDto
{
Alias = entity.Alias,
- DefaultPermissions = entity.Permissions,
+ DefaultPermissions = entity.Permissions == null ? "" : string.Join("", entity.Permissions),
Name = entity.Name
};
diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberGroupMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberGroupMapper.cs
new file mode 100644
index 0000000000..da57738403
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Mappers/MemberGroupMapper.cs
@@ -0,0 +1,34 @@
+using System.Collections.Concurrent;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Mappers
+{
+ [MapperFor(typeof (IMemberGroup))]
+ [MapperFor(typeof (MemberGroup))]
+ public sealed class MemberGroupMapper : BaseMapper
+ {
+ private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary();
+
+ //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
+ // otherwise that would fail because there is no public constructor.
+ public MemberGroupMapper()
+ {
+ BuildMap();
+ }
+
+ internal override ConcurrentDictionary PropertyInfoCache
+ {
+ get { return PropertyInfoCacheInstance; }
+ }
+
+ internal override void BuildMap()
+ {
+ CacheMap(src => src.Id, dto => dto.NodeId);
+ CacheMap(src => src.CreateDate, dto => dto.CreateDate);
+ CacheMap(src => src.CreatorId, dto => dto.UserId);
+ CacheMap(src => src.Name, dto => dto.Text);
+ CacheMap(src => src.Key, dto => dto.UniqueId);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs
index d2bbeb0792..b5cb21c683 100644
--- a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs
@@ -48,12 +48,12 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap(src => src.Email, dto => dto.Email);
CacheMap(src => src.Username, dto => dto.LoginName);
- CacheMap(src => src.Password, dto => dto.Password);
+ CacheMap(src => src.RawPasswordValue, dto => dto.Password);
CacheMap(src => src.IsApproved, dto => dto.Integer);
CacheMap(src => src.IsLockedOut, dto => dto.Integer);
CacheMap(src => src.Comments, dto => dto.Text);
- CacheMap(src => src.PasswordAnswer, dto => dto.VarChar);
+ CacheMap(src => src.RawPasswordAnswerValue, dto => dto.VarChar);
CacheMap(src => src.PasswordQuestion, dto => dto.VarChar);
CacheMap(src => src.FailedPasswordAttempts, dto => dto.Integer);
CacheMap(src => src.LastLockoutDate, dto => dto.Date);
diff --git a/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs b/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs
index 5d596fefe0..944e30c381 100644
--- a/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs
@@ -39,7 +39,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap(src => src.ParentId, dto => dto.ParentId);
CacheMap(src => src.RelationTypeId, dto => dto.RelationType);
}
-
+
#endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs
index b417b7fb53..8748bd9033 100644
--- a/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs
@@ -39,7 +39,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap(src => src.Name, dto => dto.Name);
CacheMap(src => src.ParentObjectType, dto => dto.ParentObjectType);
}
-
+
#endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Mappers/TagMapper.cs b/src/Umbraco.Core/Persistence/Mappers/TagMapper.cs
new file mode 100644
index 0000000000..74a0a05063
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Mappers/TagMapper.cs
@@ -0,0 +1,43 @@
+using System.Collections.Concurrent;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Mappers
+{
+ ///
+ /// Represents a to DTO mapper used to translate the properties of the public api
+ /// implementation to that of the database's DTO as sql: [tableName].[columnName].
+ ///
+ [MapperFor(typeof(Tag))]
+ [MapperFor(typeof(ITag))]
+ public sealed class TagMapper : BaseMapper
+ {
+ private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary();
+
+ //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
+ // otherwise that would fail because there is no public constructor.
+ public TagMapper()
+ {
+ BuildMap();
+ }
+
+ #region Overrides of BaseMapper
+
+ internal override ConcurrentDictionary PropertyInfoCache
+ {
+ get { return PropertyInfoCacheInstance; }
+ }
+
+ internal override void BuildMap()
+ {
+ if (PropertyInfoCache.IsEmpty)
+ {
+ CacheMap(src => src.Id, dto => dto.Id);
+ CacheMap(src => src.Text, dto => dto.Tag);
+ CacheMap(src => src.Group, dto => dto.Group);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs
index f8bebb32d2..9c51f53596 100644
--- a/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs
@@ -31,14 +31,14 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap(src => src.Id, dto => dto.Id);
CacheMap(src => src.Email, dto => dto.Email);
CacheMap(src => src.Username, dto => dto.Login);
- CacheMap(src => src.Password, dto => dto.Password);
+ CacheMap(src => src.RawPasswordValue, dto => dto.Password);
CacheMap(src => src.Name, dto => dto.UserName);
//NOTE: This column in the db is *not* used!
//CacheMap(src => src.DefaultPermissions, dto => dto.DefaultPermissions);
CacheMap(src => src.StartMediaId, dto => dto.MediaStartId);
CacheMap(src => src.StartContentId, dto => dto.ContentStartId);
CacheMap(src => src.IsApproved, dto => dto.Disabled);
- CacheMap(src => src.NoConsole, dto => dto.NoConsole);
+ CacheMap(src => src.IsLockedOut, dto => dto.NoConsole);
CacheMap(src => src.UserType, dto => dto.Type);
CacheMap(src => src.Language, dto => dto.UserLanguage);
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/DataLossException.cs b/src/Umbraco.Core/Persistence/Migrations/DataLossException.cs
new file mode 100644
index 0000000000..cb4ea9120a
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/DataLossException.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Umbraco.Core.Persistence.Migrations
+{
+ ///
+ /// Used if a migration has executed but the whole process has failed and cannot be rolled back
+ ///
+ internal class DataLossException : Exception
+ {
+ public DataLossException(string msg)
+ : base(msg)
+ {
+
+ }
+
+ public DataLossException(string msg, Exception inner)
+ : base(msg, inner)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
index db3fbdcd4c..ace2178a55 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
@@ -120,7 +120,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1036, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1036", SortOrder = 2, UniqueId = new Guid("2b24165f-9782-4aa3-b459-1de4a4d21f60"), Text = "Member Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1040, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1040", SortOrder = 2, UniqueId = new Guid("21e798da-e06e-4eda-a511-ed257f78d4fa"), Text = "Related Links", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1041, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1041", SortOrder = 2, UniqueId = new Guid("b6b73142-b9c1-4bf8-a16d-e1c23320b549"), Text = "Tags", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
- _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Constants.Conventions.MemberTypes.Member, NodeObjectType = new Guid(Constants.ObjectTypes.MemberType), CreateDate = DateTime.Now });
+ _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = new Guid(Constants.ObjectTypes.MemberType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1045, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1045", SortOrder = 2, UniqueId = new Guid("7E3962CC-CE20-4FFC-B661-5897A894BA7E"), Text = "Multiple Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//TODO: We're not creating these for 7.0
@@ -135,7 +135,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true });
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "mediaPhoto.png" });
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "mediaFile.png" });
- _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.Member, Icon = "icon-user", Thumbnail = "folder.png" });
+ _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.DefaultAlias, Icon = "icon-user", Thumbnail = "folder.png" });
}
private void CreateUmbracoUserData()
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
index d0d34478a4..7ad88f0739 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
@@ -79,7 +79,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
try
{
- _database.DropTable(tableName);
+ if (_database.TableExist(tableName))
+ {
+ _database.DropTable(tableName);
+ }
}
catch (Exception ex)
{
@@ -121,67 +124,60 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{
var result = new DatabaseSchemaResult();
+ //get the db index defs
+ result.DbIndexDefinitions = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(_database)
+ .Select(x => new DbIndexDefinition()
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+
foreach (var item in OrderedTables.OrderBy(x => x.Key))
{
var tableDefinition = DefinitionFactory.GetTableDefinition(item.Value);
result.TableDefinitions.Add(tableDefinition);
}
- //Check tables in configured database against tables in schema
- var tablesInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetTablesInSchema(_database).ToList();
- var tablesInSchema = result.TableDefinitions.Select(x => x.Name).ToList();
- //Add valid and invalid table differences to the result object
- var validTableDifferences = tablesInDatabase.Intersect(tablesInSchema);
- foreach (var tableName in validTableDifferences)
- {
- result.ValidTables.Add(tableName);
- }
- var invalidTableDifferences =
- tablesInDatabase.Except(tablesInSchema)
- .Union(tablesInSchema.Except(tablesInDatabase));
- foreach (var tableName in invalidTableDifferences)
- {
- result.Errors.Add(new Tuple("Table", tableName));
- }
+ ValidateDbTables(result);
- //Check columns in configured database against columns in schema
- var columnsInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetColumnsInSchema(_database);
- var columnsPerTableInDatabase = columnsInDatabase.Select(x => string.Concat(x.TableName, ",", x.ColumnName)).ToList();
- var columnsPerTableInSchema = result.TableDefinitions.SelectMany(x => x.Columns.Select(y => string.Concat(y.TableName, ",", y.Name))).ToList();
- //Add valid and invalid column differences to the result object
- var validColumnDifferences = columnsPerTableInDatabase.Intersect(columnsPerTableInSchema);
- foreach (var column in validColumnDifferences)
- {
- result.ValidColumns.Add(column);
- }
- var invalidColumnDifferences = columnsPerTableInDatabase.Except(columnsPerTableInSchema);
- foreach (var column in invalidColumnDifferences)
- {
- result.Errors.Add(new Tuple("Column", column));
- }
+ ValidateDbColumns(result);
+ ValidateDbIndexes(result);
+
+ ValidateDbConstraints(result);
+
+ return result;
+ }
+
+ private void ValidateDbConstraints(DatabaseSchemaResult result)
+ {
//MySql doesn't conform to the "normal" naming of constraints, so there is currently no point in doing these checks.
- //NOTE: At a later point we do other checks for MySql, but ideally it should be necessary to do special checks for different providers.
+ //TODO: At a later point we do other checks for MySql, but ideally it should be necessary to do special checks for different providers.
+ // ALso note that to get the constraints for MySql we have to open a connection which we currently have not.
if (SqlSyntaxContext.SqlSyntaxProvider is MySqlSyntaxProvider)
- return result;
+ return;
//Check constraints in configured database against constraints in schema
var constraintsInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(_database).DistinctBy(x => x.Item3).ToList();
- var foreignKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.StartsWith("FK_")).Select(x => x.Item3).ToList();
- var primaryKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.StartsWith("PK_")).Select(x => x.Item3).ToList();
- var indexesInDatabase = constraintsInDatabase.Where(x => x.Item3.StartsWith("IX_")).Select(x => x.Item3).ToList();
+ var foreignKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("FK_")).Select(x => x.Item3).ToList();
+ var primaryKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("PK_")).Select(x => x.Item3).ToList();
+ var indexesInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("IX_")).Select(x => x.Item3).ToList();
+ var indexesInSchema = result.TableDefinitions.SelectMany(x => x.Indexes.Select(y => y.Name)).ToList();
var unknownConstraintsInDatabase =
constraintsInDatabase.Where(
x =>
- x.Item3.StartsWith("FK_") == false && x.Item3.StartsWith("PK_") == false &&
- x.Item3.StartsWith("IX_") == false).Select(x => x.Item3).ToList();
+ x.Item3.InvariantStartsWith("FK_") == false && x.Item3.InvariantStartsWith("PK_") == false &&
+ x.Item3.InvariantStartsWith("IX_") == false).Select(x => x.Item3).ToList();
var foreignKeysInSchema = result.TableDefinitions.SelectMany(x => x.ForeignKeys.Select(y => y.Name)).ToList();
- var primaryKeysInSchema = result.TableDefinitions.SelectMany(x => x.Columns.Select(y => y.PrimaryKeyName)).ToList();
- var indexesInSchema = result.TableDefinitions.SelectMany(x => x.Indexes.Select(y => y.Name)).ToList();
+ var primaryKeysInSchema = result.TableDefinitions.SelectMany(x => x.Columns.Select(y => y.PrimaryKeyName))
+ .Where(x => x.IsNullOrWhiteSpace() == false).ToList();
+
//Add valid and invalid foreign key differences to the result object
foreach (var unknown in unknownConstraintsInDatabase)
{
- if (foreignKeysInSchema.Contains(unknown) || primaryKeysInSchema.Contains(unknown) || indexesInSchema.Contains(unknown))
+ if (foreignKeysInSchema.InvariantContains(unknown) || primaryKeysInSchema.InvariantContains(unknown) || indexesInSchema.InvariantContains(unknown))
{
result.ValidConstraints.Add(unknown);
}
@@ -190,40 +186,122 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
result.Errors.Add(new Tuple("Unknown", unknown));
}
}
- var validForeignKeyDifferences = foreignKeysInDatabase.Intersect(foreignKeysInSchema);
+
+ //Foreign keys:
+
+ var validForeignKeyDifferences = foreignKeysInDatabase.Intersect(foreignKeysInSchema, StringComparer.InvariantCultureIgnoreCase);
foreach (var foreignKey in validForeignKeyDifferences)
{
result.ValidConstraints.Add(foreignKey);
}
- var invalidForeignKeyDifferences = foreignKeysInDatabase.Except(foreignKeysInSchema);
+ var invalidForeignKeyDifferences =
+ foreignKeysInDatabase.Except(foreignKeysInSchema, StringComparer.InvariantCultureIgnoreCase)
+ .Union(foreignKeysInSchema.Except(foreignKeysInDatabase, StringComparer.InvariantCultureIgnoreCase));
foreach (var foreignKey in invalidForeignKeyDifferences)
{
result.Errors.Add(new Tuple("Constraint", foreignKey));
}
+
+
+ //Primary keys:
+
//Add valid and invalid primary key differences to the result object
- var validPrimaryKeyDifferences = primaryKeysInDatabase.Intersect(primaryKeysInSchema);
+ var validPrimaryKeyDifferences = primaryKeysInDatabase.Intersect(primaryKeysInSchema, StringComparer.InvariantCultureIgnoreCase);
foreach (var primaryKey in validPrimaryKeyDifferences)
{
result.ValidConstraints.Add(primaryKey);
}
- var invalidPrimaryKeyDifferences = primaryKeysInDatabase.Except(primaryKeysInSchema);
+ var invalidPrimaryKeyDifferences =
+ primaryKeysInDatabase.Except(primaryKeysInSchema, StringComparer.InvariantCultureIgnoreCase)
+ .Union(primaryKeysInSchema.Except(primaryKeysInDatabase, StringComparer.InvariantCultureIgnoreCase));
foreach (var primaryKey in invalidPrimaryKeyDifferences)
{
result.Errors.Add(new Tuple("Constraint", primaryKey));
}
+
+ //Constaints:
+
+ //NOTE: SD: The colIndex checks above should really take care of this but I need to keep this here because it was here before
+ // and some schema validation checks might rely on this data remaining here!
//Add valid and invalid index differences to the result object
- var validIndexDifferences = indexesInDatabase.Intersect(indexesInSchema);
+ var validIndexDifferences = indexesInDatabase.Intersect(indexesInSchema, StringComparer.InvariantCultureIgnoreCase);
foreach (var index in validIndexDifferences)
{
result.ValidConstraints.Add(index);
}
- var invalidIndexDifferences = indexesInDatabase.Except(indexesInSchema);
+ var invalidIndexDifferences =
+ indexesInDatabase.Except(indexesInSchema, StringComparer.InvariantCultureIgnoreCase)
+ .Union(indexesInSchema.Except(indexesInDatabase, StringComparer.InvariantCultureIgnoreCase));
foreach (var index in invalidIndexDifferences)
{
result.Errors.Add(new Tuple("Constraint", index));
}
+ }
- return result;
+ private void ValidateDbColumns(DatabaseSchemaResult result)
+ {
+ //Check columns in configured database against columns in schema
+ var columnsInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetColumnsInSchema(_database);
+ var columnsPerTableInDatabase = columnsInDatabase.Select(x => string.Concat(x.TableName, ",", x.ColumnName)).ToList();
+ var columnsPerTableInSchema = result.TableDefinitions.SelectMany(x => x.Columns.Select(y => string.Concat(y.TableName, ",", y.Name))).ToList();
+ //Add valid and invalid column differences to the result object
+ var validColumnDifferences = columnsPerTableInDatabase.Intersect(columnsPerTableInSchema, StringComparer.InvariantCultureIgnoreCase);
+ foreach (var column in validColumnDifferences)
+ {
+ result.ValidColumns.Add(column);
+ }
+
+ var invalidColumnDifferences =
+ columnsPerTableInDatabase.Except(columnsPerTableInSchema, StringComparer.InvariantCultureIgnoreCase)
+ .Union(columnsPerTableInSchema.Except(columnsPerTableInDatabase, StringComparer.InvariantCultureIgnoreCase));
+ foreach (var column in invalidColumnDifferences)
+ {
+ result.Errors.Add(new Tuple("Column", column));
+ }
+ }
+
+ private void ValidateDbTables(DatabaseSchemaResult result)
+ {
+ //Check tables in configured database against tables in schema
+ var tablesInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetTablesInSchema(_database).ToList();
+ var tablesInSchema = result.TableDefinitions.Select(x => x.Name).ToList();
+ //Add valid and invalid table differences to the result object
+ var validTableDifferences = tablesInDatabase.Intersect(tablesInSchema, StringComparer.InvariantCultureIgnoreCase);
+ foreach (var tableName in validTableDifferences)
+ {
+ result.ValidTables.Add(tableName);
+ }
+
+ var invalidTableDifferences =
+ tablesInDatabase.Except(tablesInSchema, StringComparer.InvariantCultureIgnoreCase)
+ .Union(tablesInSchema.Except(tablesInDatabase, StringComparer.InvariantCultureIgnoreCase));
+ foreach (var tableName in invalidTableDifferences)
+ {
+ result.Errors.Add(new Tuple("Table", tableName));
+ }
+ }
+
+ private void ValidateDbIndexes(DatabaseSchemaResult result)
+ {
+ //These are just column indexes NOT constraints or Keys
+ //var colIndexesInDatabase = result.DbIndexDefinitions.Where(x => x.IndexName.InvariantStartsWith("IX_")).Select(x => x.IndexName).ToList();
+ var colIndexesInDatabase = result.DbIndexDefinitions.Select(x => x.IndexName).ToList();
+ var indexesInSchema = result.TableDefinitions.SelectMany(x => x.Indexes.Select(y => y.Name)).ToList();
+
+ //Add valid and invalid index differences to the result object
+ var validColIndexDifferences = colIndexesInDatabase.Intersect(indexesInSchema, StringComparer.InvariantCultureIgnoreCase);
+ foreach (var index in validColIndexDifferences)
+ {
+ result.ValidIndexes.Add(index);
+ }
+
+ var invalidColIndexDifferences =
+ colIndexesInDatabase.Except(indexesInSchema, StringComparer.InvariantCultureIgnoreCase)
+ .Union(indexesInSchema.Except(colIndexesInDatabase, StringComparer.InvariantCultureIgnoreCase));
+ foreach (var index in invalidColIndexDifferences)
+ {
+ result.Errors.Add(new Tuple("Index", index));
+ }
}
#region Events
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
index bc7f955902..9151ffd598 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -17,6 +17,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
ValidTables = new List();
ValidColumns = new List();
ValidConstraints = new List();
+ ValidIndexes = new List();
}
public List> Errors { get; set; }
@@ -29,6 +30,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
public List ValidConstraints { get; set; }
+ public List ValidIndexes { get; set; }
+
+ internal IEnumerable DbIndexDefinitions { get; set; }
+
///
/// Determines the version of the currently installed database.
///
@@ -39,43 +44,53 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
public Version DetermineInstalledVersion()
{
//If (ValidTables.Count == 0) database is empty and we return -> new Version(0, 0, 0);
- if(ValidTables.Count == 0)
+ if (ValidTables.Count == 0)
return new Version(0, 0, 0);
//If Errors is empty or if TableDefinitions tables + columns correspond to valid tables + columns then we're at current version
- if (!Errors.Any() ||
+ if (Errors.Any() == false ||
(TableDefinitions.All(x => ValidTables.Contains(x.Name))
&& TableDefinitions.SelectMany(definition => definition.Columns).All(x => ValidColumns.Contains(x.Name))))
return UmbracoVersion.Current;
//If Errors contains umbracoApp or umbracoAppTree its pre-6.0.0 -> new Version(4, 10, 0);
- if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.Equals("umbracoApp") || x.Item2.Equals("umbracoAppTree"))))
+ if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoApp") || x.Item2.InvariantEquals("umbracoAppTree"))))
{
//If Errors contains umbracoUser2app or umbracoAppTree foreignkey to umbracoApp exists its pre-4.8.0 -> new Version(4, 7, 0);
if (Errors.Any(x =>
x.Item1.Equals("Constraint")
- && (x.Item2.Contains("umbracoUser2app_umbracoApp")
- || x.Item2.Contains("umbracoAppTree_umbracoApp"))))
+ && (x.Item2.InvariantContains("umbracoUser2app_umbracoApp")
+ || x.Item2.InvariantContains("umbracoAppTree_umbracoApp"))))
{
return new Version(4, 7, 0);
}
return new Version(4, 9, 0);
}
-
+
//if the error is for umbracoServer
- if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.Equals("umbracoServer"))))
+ if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoServer"))))
{
return new Version(6, 0, 0);
}
- //if the error indicates a problem with the column cmsMacroProperty.macroPropertyType then it is not version 7 and the
- // last db change we made was the umbracoServer in 6.1
- if (Errors.Any(x => x.Item1.Equals("Column") && (x.Item2.Equals("cmsMacroProperty,macroPropertyType"))))
+ //if the error indicates a problem with the column cmsMacroProperty.macroPropertyType then it is not version 7
+ // since these columns get removed in v7
+ if (Errors.Any(x => x.Item1.Equals("Column") && (x.Item2.InvariantEquals("cmsMacroProperty,macroPropertyType"))))
{
- return new Version(6, 1, 0);
+ //if the error is for this IX_umbracoNodeTrashed which is added in 6.2 AND in 7.1 but we do not have the above columns
+ // then it must mean that we aren't on 6.2 so must be 6.1
+ if (Errors.Any(x => x.Item1.Equals("Index") && (x.Item2.InvariantEquals("IX_umbracoNodeTrashed"))))
+ {
+ return new Version(6, 1, 0);
+ }
+ else
+ {
+ //if there are no errors for that index, then the person must have 6.2 installed
+ return new Version(6, 2, 0);
+ }
}
-
+
return UmbracoVersion.Current;
}
@@ -113,6 +128,13 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
sb.AppendLine(string.Join(",", Errors.Where(x => x.Item1.Equals("Constraint")).Select(x => x.Item2)));
sb.AppendLine(" ");
}
+ //Index error summary
+ if (Errors.Any(x => x.Item1.Equals("Index")))
+ {
+ sb.AppendLine("The following indexes were found in the database, but are not in the current schema:");
+ sb.AppendLine(string.Join(",", Errors.Where(x => x.Item1.Equals("Index")).Select(x => x.Item2)));
+ sb.AppendLine(" ");
+ }
//Unknown constraint error summary
if (Errors.Any(x => x.Item1.Equals("Unknown")))
{
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationAttribute.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationAttribute.cs
index b7e16614e6..b42b4aa6b8 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationAttribute.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationAttribute.cs
@@ -6,8 +6,8 @@ namespace Umbraco.Core.Persistence.Migrations
/// Represents the Migration attribute, which is used to mark classes as
/// database migrations with Up/Down methods for pushing changes UP or pulling them DOWN.
///
- [AttributeUsage(AttributeTargets.Class)]
- public class MigrationAttribute : Attribute
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+ public sealed class MigrationAttribute : Attribute
{
public MigrationAttribute(string targetVersion, int sortOrder, string product)
{
@@ -16,8 +16,21 @@ namespace Umbraco.Core.Persistence.Migrations
ProductName = product;
}
+ public MigrationAttribute(string minimumCurrentVersion, string targetVersion, int sortOrder, string product)
+ {
+ TargetVersion = new Version(targetVersion);
+ MinimumCurrentVersion = new Version(minimumCurrentVersion);
+ SortOrder = sortOrder;
+ ProductName = product;
+ }
+
///
- /// Gets or sets the target version of this migration.
+ /// Gets the minimum current version for which this migration is allowed to execute
+ ///
+ public Version MinimumCurrentVersion { get; private set; }
+
+ ///
+ /// Gets the target version of this migration.
///
public Version TargetVersion { get; private set; }
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs
index 9d7d845646..3d28e99c56 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs
@@ -27,26 +27,8 @@ namespace Umbraco.Core.Persistence.Migrations
///
public IEnumerable Migrations
{
- get { return GetSortedValues(); }
+ get { return Values; }
}
- ///
- /// Override how we determine object weight, for this resolver we use the MigrationAttribute attribute
- ///
- ///
- ///
- protected override int GetObjectWeight(object o)
- {
- var type = o.GetType();
- var attr = type.GetCustomAttribute(true);
- return attr == null ? DefaultPluginWeight : attr.SortOrder;
- }
-
- protected override int DefaultPluginWeight
- {
- get { return 0; } //set's the default to 0
- set { base.DefaultPluginWeight = value; }
- }
-
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
index ad5fcdebc5..cacfd87fbd 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
@@ -3,22 +3,23 @@ using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase;
namespace Umbraco.Core.Persistence.Migrations
{
- ///
+ ///
/// Represents the Migration Runner, which is used to apply migrations to
/// the umbraco database.
///
public class MigrationRunner
{
- private readonly Version _configuredVersion;
+ private readonly Version _currentVersion;
private readonly Version _targetVersion;
private readonly string _productName;
- public MigrationRunner(Version configuredVersion, Version targetVersion, string productName)
+ public MigrationRunner(Version currentVersion, Version targetVersion, string productName)
{
- _configuredVersion = configuredVersion;
+ _currentVersion = currentVersion;
_targetVersion = targetVersion;
_productName = productName;
}
@@ -45,33 +46,50 @@ namespace Umbraco.Core.Persistence.Migrations
{
LogHelper.Info("Initializing database migrations");
- var foundMigrations = MigrationResolver.Current.Migrations;
+ var foundMigrations = MigrationResolver.Current.Migrations.ToArray();
+ //filter all non-schema migrations
var migrations = isUpgrade
? OrderedUpgradeMigrations(foundMigrations).ToList()
: OrderedDowngradeMigrations(foundMigrations).ToList();
- if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _configuredVersion, _targetVersion, true), this))
+ //SD: Why do we want this?
+ if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this))
return false;
//Loop through migrations to generate sql
- var context = new MigrationContext(databaseProvider, database);
- foreach (MigrationBase migration in migrations)
+ var migrationContext = InitializeMigrations(migrations, database, databaseProvider, isUpgrade);
+
+ try
{
- if (isUpgrade)
+ ExecuteMigrations(migrationContext, database);
+ }
+ catch (Exception ex)
+ {
+ //if this fails then the transaction will be rolled back, BUT if we are using MySql this is not the case,
+ //since it does not support schema changes in a transaction, see: http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html
+ //so in that case we have to downgrade
+
+ if (databaseProvider == DatabaseProviders.MySql)
{
- migration.GetUpExpressions(context);
- LogHelper.Info(string.Format("Added UPGRADE migration '{0}' to context", migration.GetType().Name));
- }
- else
- {
- migration.GetDownExpressions(context);
- LogHelper.Info(string.Format("Added DOWNGRADE migration '{0}' to context", migration.GetType().Name));
+ throw new DataLossException(
+ "An error occurred running a schema migration but the changes could not be rolled back. Error: " + ex.Message + ". In some cases, it may be required that the database be restored to it's original state before running this upgrade process again.",
+ ex);
}
+
+ //continue throwing the exception
+ throw;
}
+ Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, false), this);
+
+ return true;
+ }
+
+ private void ExecuteMigrations(IMigrationContext context, Database database)
+ {
//Transactional execution of the sql that was generated from the found migrations
- using (Transaction transaction = database.GetTransaction())
+ using (var transaction = database.GetTransaction())
{
int i = 1;
foreach (var expression in context.Expressions)
@@ -90,38 +108,89 @@ namespace Umbraco.Core.Persistence.Migrations
transaction.Complete();
}
+ }
- Migrated.RaiseEvent(new MigrationEventArgs(migrations, context, _configuredVersion, _targetVersion, false), this);
+ internal MigrationContext InitializeMigrations(List migrations, Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
+ {
+ //Loop through migrations to generate sql
+ var context = new MigrationContext(databaseProvider, database);
+
+ foreach (var migration in migrations)
+ {
+ var baseMigration = migration as MigrationBase;
+ if (baseMigration != null)
+ {
+ if (isUpgrade)
+ {
+ baseMigration.GetUpExpressions(context);
+ LogHelper.Info(string.Format("Added UPGRADE migration '{0}' to context", baseMigration.GetType().Name));
+ }
+ else
+ {
+ baseMigration.GetDownExpressions(context);
+ LogHelper.Info(string.Format("Added DOWNGRADE migration '{0}' to context", baseMigration.GetType().Name));
+ }
+ }
+ else
+ {
+ //this is just a normal migration so we can only call Up/Down
+ if (isUpgrade)
+ {
+ migration.Up();
+ LogHelper.Info(string.Format("Added UPGRADE migration '{0}' to context", migration.GetType().Name));
+ }
+ else
+ {
+ migration.Down();
+ LogHelper.Info(string.Format("Added DOWNGRADE migration '{0}' to context", migration.GetType().Name));
+ }
+ }
+ }
- return true;
- }
+ return context;
+ }
+ ///
+ /// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
+ ///
+ ///
+ ///
internal IEnumerable OrderedUpgradeMigrations(IEnumerable foundMigrations)
{
var migrations = (from migration in foundMigrations
- let migrationAttribute = migration.GetType().FirstAttribute()
- where migrationAttribute != null
- where
- migrationAttribute.TargetVersion > _configuredVersion &&
- migrationAttribute.TargetVersion <= _targetVersion &&
- migrationAttribute.ProductName == _productName
- orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
- select migration);
+ let migrationAttributes = migration.GetType().GetCustomAttributes(false)
+ from migrationAttribute in migrationAttributes
+ where migrationAttribute != null
+ where
+ migrationAttribute.TargetVersion > _currentVersion &&
+ migrationAttribute.TargetVersion <= _targetVersion &&
+ migrationAttribute.ProductName == _productName &&
+ //filter if the migration specifies a minimum current version for which to execute
+ (migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
+ orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
+ select migration).Distinct();
return migrations;
}
+ ///
+ /// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
+ ///
+ ///
+ ///
public IEnumerable OrderedDowngradeMigrations(IEnumerable foundMigrations)
{
var migrations = (from migration in foundMigrations
- let migrationAttribute = migration.GetType().FirstAttribute()
- where migrationAttribute != null
- where
- migrationAttribute.TargetVersion > _configuredVersion &&
- migrationAttribute.TargetVersion <= _targetVersion &&
- migrationAttribute.ProductName == _productName
- orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
-
- select migration);
+ let migrationAttributes = migration.GetType().GetCustomAttributes(false)
+ from migrationAttribute in migrationAttributes
+ where migrationAttribute != null
+ where
+ migrationAttribute.TargetVersion > _currentVersion &&
+ migrationAttribute.TargetVersion <= _targetVersion &&
+ migrationAttribute.ProductName == _productName &&
+ //filter if the migration specifies a minimum current version for which to execute
+ (migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
+ orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
+ select migration).Distinct();
return migrations;
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs
index 75eb5d1d1f..76a6a35db3 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -41,7 +42,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
if (string.IsNullOrEmpty(ForeignKey.Name))
{
- ForeignKey.Name = string.Format("FK_{0}_{1}", ForeignKey.ForeignTable, ForeignKey.PrimaryTable);
+ ForeignKey.Name = string.Format("FK_{0}_{1}_{2}", ForeignKey.ForeignTable, ForeignKey.PrimaryTable, ForeignKey.PrimaryColumns.First());
}
return string.Format(SqlSyntaxContext.SqlSyntaxProvider.DeleteConstraint,
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs
index df70d81b0a..6521d043a7 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs
@@ -1,4 +1,5 @@
-using Umbraco.Core.Persistence.Migrations.Syntax.Delete.Column;
+using System;
+using Umbraco.Core.Persistence.Migrations.Syntax.Delete.Column;
using Umbraco.Core.Persistence.Migrations.Syntax.Delete.Constraint;
using Umbraco.Core.Persistence.Migrations.Syntax.Delete.DefaultConstraint;
using Umbraco.Core.Persistence.Migrations.Syntax.Delete.ForeignKey;
@@ -11,6 +12,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete
void Table(string tableName);
IDeleteColumnFromTableSyntax Column(string columnName);
IDeleteForeignKeyFromTableSyntax ForeignKey();
+ [Obsolete("Do not use this construct as it does not work with MySql, use the syntax: Delete.ForeignKey().FromTable(\"umbracoUser2app\").ForeignColumn(... instead")]
IDeleteForeignKeyOnTableSyntax ForeignKey(string foreignKeyName);
IDeleteDataSyntax FromTable(string tableName);
IDeleteIndexForTableSyntax Index();
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs
index efcb5d2415..244884017c 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs
@@ -8,9 +8,21 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero
{
public override void Up()
{
- Delete.ForeignKey().FromTable("umbracoUser2app").ForeignColumn("app").ToTable("umbracoApp").PrimaryColumn("appAlias");
+ //This will work on mysql and should work on mssql however the old keys were not named consistently with how the keys are
+ // structured now. So we need to do a check and manually remove them based on their old aliases.
- Delete.ForeignKey().FromTable("umbracoAppTree").ForeignColumn("appAlias").ToTable("umbracoApp").PrimaryColumn("appAlias");
+ if (this.Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
+ {
+ Delete.ForeignKey().FromTable("umbracoUser2app").ForeignColumn("app").ToTable("umbracoApp").PrimaryColumn("appAlias");
+ Delete.ForeignKey().FromTable("umbracoAppTree").ForeignColumn("appAlias").ToTable("umbracoApp").PrimaryColumn("appAlias");
+ }
+ else
+ {
+ //These are the old aliases
+ Delete.ForeignKey("FK_umbracoUser2app_umbracoApp").OnTable("umbracoUser2app");
+ Delete.ForeignKey("FK_umbracoUser2app_umbracoUser").OnTable("umbracoUser2app");
+ }
+
}
public override void Down()
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs
index 7ba5224e5a..20480ad534 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs
@@ -1,5 +1,8 @@
using System;
+using System.Linq;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
@@ -9,18 +12,45 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
[Migration("7.0.0", 5, GlobalSettings.UmbracoMigrationName)]
public class AddIndexToCmsMacroPropertyTable : MigrationBase
{
+ private readonly bool _skipIndexCheck;
+
+ internal AddIndexToCmsMacroPropertyTable(bool skipIndexCheck)
+ {
+ _skipIndexCheck = skipIndexCheck;
+ }
+
+ public AddIndexToCmsMacroPropertyTable()
+ {
+
+ }
+
public override void Up()
{
- Create.Index("IX_cmsMacroProperty_Alias").OnTable("cmsMacroProperty")
+ var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[]{} : SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
+ .Select(x => new DbIndexDefinition()
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+
+ //make sure it doesn't already exist
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMacroProperty_Alias")) == false)
+ {
+ Create.Index("IX_cmsMacroProperty_Alias").OnTable("cmsMacroProperty")
.OnColumn("macro")
.Ascending()
.OnColumn("macroPropertyAlias")
.Unique();
+ }
+
+
}
public override void Down()
{
- throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version");
+ Delete.Index("IX_cmsMacroProperty_Alias").OnTable("cmsMacroProperty");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs
index 3b4f1f8817..1be642b9d1 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs
@@ -1,5 +1,9 @@
using System;
+using System.CodeDom;
+using System.Linq;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
@@ -9,14 +13,40 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
[Migration("7.0.0", 4, GlobalSettings.UmbracoMigrationName)]
public class AddIndexToCmsMacroTable : MigrationBase
{
+ private readonly bool _skipIndexCheck;
+
+ internal AddIndexToCmsMacroTable(bool skipIndexCheck)
+ {
+ _skipIndexCheck = skipIndexCheck;
+ }
+
+ public AddIndexToCmsMacroTable()
+ {
+
+ }
+
public override void Up()
{
- Create.Index("IX_cmsMacro_Alias").OnTable("cmsMacro").OnColumn("macroAlias").Unique();
+ var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[] { } : SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
+ .Select(x => new DbIndexDefinition()
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+
+ //make sure it doesn't already exist
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMacro_Alias")) == false)
+ {
+ Create.Index("IX_cmsMacro_Alias").OnTable("cmsMacro").OnColumn("macroAlias").Unique();
+ }
+
}
public override void Down()
{
- throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version");
+ Delete.Index("IX_cmsMacro_Alias").OnTable("cmsMacro");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs
index 93bb3e4193..06fe7456d9 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
public override void Down()
{
- throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version");
+ Delete.Column("propertyEditorAlias").FromTable("cmsDataType");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs
index 2ab9673fdc..93afeee7bb 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs
@@ -22,8 +22,8 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
Delete.Column("macroPropertyHidden").FromTable("cmsMacroProperty");
- Delete.ForeignKey("FK_cmsMacroProperty_cmsMacroPropertyType_id").OnTable("cmsMacroProperty");
-
+ Delete.ForeignKey().FromTable("cmsMacroProperty").ForeignColumn("macroPropertyType").ToTable("cmsMacroPropertyType").PrimaryColumn("id");
+
Alter.Table("cmsMacroProperty").AddColumn("editorAlias").AsString(255).NotNullable().WithDefaultValue("");
//we need to get the data and create the migration scripts before we change the actual schema bits below!
@@ -52,7 +52,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
public override void Down()
{
- throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version");
+ throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs
index 4a549c7af3..07c2a02475 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs
@@ -4,6 +4,8 @@ using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
@@ -25,12 +27,28 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
//create a new col which we will make a foreign key, but first needs to be populated with data.
Alter.Table("cmsTagRelationship").AddColumn("propertyTypeId").AsInt32().Nullable();
-
+
//drop the foreign key on umbracoNode. Must drop foreign key first before primary key can be removed in MySql.
- Delete.ForeignKey("FK_cmsTagRelationship_umbracoNode_id").OnTable("cmsTagRelationship");
- //we need to drop the primary key
- Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship");
+ Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("nodeId").ToTable("umbracoNode").PrimaryColumn("id");
+
+ //we need to drop the primary key, this is sql specific since MySQL has never had primary keys on this table
+ // at least since 6.0 and the new installation way but perhaps it had them way back in 4.x so we need to check
+ // it exists before trying to drop it.
+ if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
+ {
+ var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
+ //this will let us know if this pk exists on this table
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsTagRelationship") && x.Item3.InvariantEquals("PRIMARY")) > 0)
+ {
+ Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship");
+ }
+ }
+ else
+ {
+ Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship");
+ }
+
}
private void Upgrade()
@@ -115,7 +133,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
public override void Down()
{
- throw new NotSupportedException();
+ throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified");
}
///
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs
index 581fd7ef07..b52dd4baed 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs
@@ -1,6 +1,9 @@
using System;
using System.Data;
+using System.Linq;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
@@ -9,6 +12,15 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
public override void Up()
{
+ var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
+ .Select(x => new DbIndexDefinition()
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+
//add a foreign key to the parent id column too!
Create.ForeignKey("FK_cmsTags_cmsTags")
.FromTable("cmsTags")
@@ -16,15 +28,20 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
.ToTable("cmsTags")
.PrimaryColumn("id")
.OnDelete(Rule.None)
- .OnUpdate(Rule.None);
+ .OnUpdate(Rule.None);
- //add an index to tag/group since it's queried often
- Create.Index("IX_cmsTags").OnTable("cmsTags").OnColumn("tag").Ascending().OnColumn("group").Ascending().WithOptions().NonClustered();
+ //make sure it doesn't already exist
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsTags")) == false)
+ {
+ //add an index to tag/group since it's queried often
+ Create.Index("IX_cmsTags").OnTable("cmsTags").OnColumn("tag").Ascending().OnColumn("group").Ascending().WithOptions().NonClustered();
+ }
+
}
public override void Down()
{
- throw new NotImplementedException();
+ throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs
index 4c2acdd5ae..a9b8e382d0 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
public override void Down()
{
- throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version");
+ throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs
index df3a4f0450..2148b0ccbc 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
public override void Down()
{
- throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version");
+ throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs
index c7adc2af89..9264fdc646 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
public override void Down()
{
- throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version");
+ throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs
index 976e2d05b1..12ad0b4f34 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs
@@ -55,7 +55,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
public override void Down()
{
- throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version");
+ throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs
index d16f64cc90..776aa47c12 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs
@@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using System.Xml;
+using System.Xml.Linq;
using Newtonsoft.Json;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
@@ -26,81 +27,86 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
if (database != null)
{
- try
+ var dtSql = new Sql().Select("nodeId").From().Where(dto => dto.PropertyEditorAlias == Constants.PropertyEditors.RelatedLinksAlias);
+ var dataTypeIds = database.Fetch(dtSql);
+
+ var propertyData =
+ database.Fetch(
+ "WHERE propertyTypeId in (SELECT id from cmsPropertyType where dataTypeID IN (@dataTypeIds))", new { dataTypeIds = dataTypeIds });
+ if (!propertyData.Any()) return string.Empty;
+
+ var nodesIdsWithProperty = propertyData.Select(x => x.NodeId).Distinct();
+ var cmsContentXmlEntries = database.Fetch(
+ "WHERE nodeId in (@nodeIds)", new { nodeIds = nodesIdsWithProperty });
+ var propertyTypeIds = propertyData.Select(x => x.PropertyTypeId).Distinct();
+ var propertyTypes = database.Fetch(
+ "WHERE id in (@propertyTypeIds)", new { propertyTypeIds = propertyTypeIds });
+
+ foreach (var data in propertyData)
{
- var propertyData =
- database.Fetch(
- "WHERE propertyTypeId in (SELECT id from cmsPropertyType where dataTypeID = 1040)");
- foreach (var data in propertyData)
+ if (string.IsNullOrEmpty(data.Text) == false)
{
- if (!string.IsNullOrEmpty(data.Text))
+ //fetch the current data (that's in xml format)
+ var xml = new XmlDocument();
+ xml.LoadXml(data.Text);
+
+ var links = new List();
+
+ //loop all the stored links
+ foreach (XmlNode node in xml.DocumentElement.ChildNodes)
{
- //var cs = ApplicationContext.Current.Services.ContentService;
+ var title = node.Attributes["title"].Value;
+ var type = node.Attributes["type"].Value;
+ var newwindow = node.Attributes["newwindow"].Value.Equals("1") ? true : false;
+ var lnk = node.Attributes["link"].Value;
- //fetch the current data (that's in xml format)
- var xml = new XmlDocument();
- xml.LoadXml(data.Text);
+ //create the links in the format the new prop editor expects it to be
+ var link = new ExpandoObject() as IDictionary;
+ link.Add("title", title);
+ link.Add("caption", title);
+ link.Add("link", lnk);
+ link.Add("newWindow", newwindow);
+ link.Add("type", type.Equals("internal") ? "internal" : "external");
+ link.Add("internal", type.Equals("internal") ? lnk : null);
- if (xml != null)
+ link.Add("edit", false);
+ link.Add("isInternal", type.Equals("internal"));
+
+ links.Add((ExpandoObject)link);
+ }
+
+ //store the serialized data
+ data.Text = JsonConvert.SerializeObject(links);
+
+ database.Update(data);
+
+ //now we need to update the cmsContentXml table
+ var propertyType = propertyTypes.SingleOrDefault(x => x.Id == data.PropertyTypeId);
+ if (propertyType != null)
+ {
+ var xmlItem = cmsContentXmlEntries.SingleOrDefault(x => x.NodeId == data.NodeId);
+ if (xmlItem != null)
{
- var links = new List();
-
- //loop all the stored links
- foreach (XmlNode node in xml.DocumentElement.ChildNodes)
+ var x = XElement.Parse(xmlItem.Xml);
+ var prop = x.Element(propertyType.Alias);
+ if (prop != null)
{
- var title = node.Attributes["title"].Value;
- var type = node.Attributes["type"].Value;
- var newwindow = node.Attributes["newwindow"].Value.Equals("1") ? true : false;
- var lnk = node.Attributes["link"].Value;
-
- //create the links in the format the new prop editor expects it to be
- var link = new ExpandoObject() as IDictionary;
- link.Add("title", title);
- link.Add("caption", title);
- link.Add("link", lnk);
- link.Add("newWindow", newwindow);
- link.Add("type", type.Equals("internal") ? "internal" : "external");
- link.Add("internal", type.Equals("internal") ? lnk : null);
-
- link.Add("edit", false);
- link.Add("isInternal", type.Equals("internal"));
-
- //try
- //{
- // if (type.Equals("internal"))
- // {
- // int nodeId;
- // if (int.TryParse(lnk, out nodeId))
- // link.Add("internalName", cs.GetById(nodeId).Name);
- // }
- //}
- //catch (Exception ex)
- //{
- // LogHelper.Error("Exception was thrown when trying to update related links property data, fetching internal node id", ex);
- //}
-
- links.Add((ExpandoObject) link);
+ prop.ReplaceAll(new XCData(data.Text));
+ database.Update(xmlItem);
}
-
- //store the serialized data
- data.Text = JsonConvert.SerializeObject(links);
-
- database.Update(data);
}
}
+
+
}
}
- catch (Exception ex)
- {
- LogHelper.Error("Exception was thrown when trying to update related links property data", ex);
- }
}
return string.Empty;
}
public override void Down()
{
- throw new NotImplementedException();
+ throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified");
}
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs
new file mode 100644
index 0000000000..bb5bf56386
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs
@@ -0,0 +1,38 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenOneZero
+{
+ //see: http://issues.umbraco.org/issue/U4-4430 - Except this one is specific to 7 because
+ // we've modified the tagRelationship PK already.
+ //We have to target this specifically however to ensure this DOES NOT execute if upgrading from a version previous to 7.0,
+ // this is because when the 7.0.0 migrations are executed, this primary key get's created so if this migration is also executed
+ // we will get exceptions because it is trying to create the PK two times.
+
+ [Migration("7.0.0", "7.1.0", 0, GlobalSettings.UmbracoMigrationName)]
+ public class AssignMissingPrimaryForMySqlKeys : MigrationBase
+ {
+ public override void Up()
+ {
+ if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
+ {
+ var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
+
+ //This should be 3 because this table has 3 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsTagRelationship") && x.Item3.InvariantEquals("PRIMARY")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsTagRelationship")
+ .OnTable("cmsTagRelationship")
+ .Columns(new[] { "nodeId", "propertyTypeId", "tagId" });
+ }
+
+ }
+ }
+
+ public override void Down()
+ {
+ //don't do anything, these keys should have always existed!
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs
index 567f641e5c..05edf00062 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs
@@ -2,6 +2,7 @@
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix
{
+
[Migration("6.0.0", 10, GlobalSettings.UmbracoMigrationName)]
public class DeleteAppTables : MigrationBase
{
@@ -14,6 +15,8 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix
public override void Down()
{
+ //This cannot be rolled back!!
+ throw new DataLossException("Cannot rollback migration " + typeof(DeleteAppTables) + " the db tables umbracoAppTree and umbracoApp have been droppped");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs
index 2bd03f15bc..25efd73082 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs
@@ -10,15 +10,15 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix
{
Alter.Table("cmsPropertyTypeGroup").AddColumn("parentGroupId").AsInt16().Nullable();
- Create.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup")
+ Create.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup_id")
.FromTable("cmsPropertyTypeGroup").ForeignColumn("parentGroupId")
.ToTable("cmsPropertyTypeGroup").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None);
}
public override void Down()
{
- Delete.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup").OnTable("cmsPropertyTypeGroup");
-
+ Delete.ForeignKey().FromTable("cmsPropertyTypeGroup").ForeignColumn("parentGroupId").ToTable("cmsPropertyTypeGroup").PrimaryColumn("id");
+
Delete.Column("parentGroupId").FromTable("cmsPropertyTypeGroup");
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs
index 757e5fd4f0..5c39f92b21 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs
@@ -13,11 +13,13 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixOneZero
{
public override void Up()
{
- base.Context.Database.CreateTable();
+ //NOTE: This isn't the correct way to do this but to manually create this table with the Create syntax is a pain in the arse
+ Context.Database.CreateTable();
}
public override void Down()
{
+ Delete.Table("umbracoServer");
}
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs
index fa1b24d1f6..5c36826d02 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs
@@ -4,7 +4,8 @@ using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
{
- [Migration("6.2.0", 1, GlobalSettings.UmbracoMigrationName)]
+ [Migration("7.1.0", 3, GlobalSettings.UmbracoMigrationName)]
+ [Migration("6.2.0", 3, GlobalSettings.UmbracoMigrationName)]
public class AddChangeDocumentTypePermission : MigrationBase
{
public override void Up()
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs
index e4d2496903..446c35d3ad 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs
@@ -1,37 +1,65 @@
using System;
+using System.Linq;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.Migrations.Initial;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
{
- [Migration("6.2.0", 0, GlobalSettings.UmbracoMigrationName)]
+ [Migration("7.1.0", 1, GlobalSettings.UmbracoMigrationName)]
+ [Migration("6.2.0", 1, GlobalSettings.UmbracoMigrationName)]
public class AdditionalIndexesAndKeys : MigrationBase
{
public override void Up()
- {
- Create.Index("IX_umbracoNodeTrashed").OnTable("umbracoNode").OnColumn("trashed").Ascending().WithOptions().NonClustered();
- Create.Index("IX_cmsContentVersion_ContentId").OnTable("cmsContentVersion").OnColumn("ContentId").Ascending().WithOptions().NonClustered();
- Create.Index("IX_cmsDocument_published").OnTable("cmsDocument").OnColumn("published").Ascending().WithOptions().NonClustered();
- Create.Index("IX_cmsDocument_newest").OnTable("cmsDocument").OnColumn("newest").Ascending().WithOptions().NonClustered();
+ {
+
+ var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
+ .Select(x => new DbIndexDefinition()
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+
+ //do not create any indexes if they already exist in the database
+
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeTrashed")) == false)
+ {
+ Create.Index("IX_umbracoNodeTrashed").OnTable("umbracoNode").OnColumn("trashed").Ascending().WithOptions().NonClustered();
+ }
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsContentVersion_ContentId")) == false)
+ {
+ Create.Index("IX_cmsContentVersion_ContentId").OnTable("cmsContentVersion").OnColumn("ContentId").Ascending().WithOptions().NonClustered();
+ }
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsDocument_published")) == false)
+ {
+ Create.Index("IX_cmsDocument_published").OnTable("cmsDocument").OnColumn("published").Ascending().WithOptions().NonClustered();
+ }
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsDocument_newest")) == false)
+ {
+ Create.Index("IX_cmsDocument_newest").OnTable("cmsDocument").OnColumn("newest").Ascending().WithOptions().NonClustered();
+ }
+
+ //we want to drop the umbracoUserLogins_Index index since it is named incorrectly and then re-create it so
+ // it follows the standard naming convention
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("umbracoUserLogins_Index")))
+ {
+ Delete.Index("umbracoUserLogins_Index").OnTable("umbracoUserLogins");
+ }
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoUserLogins_Index")) == false)
+ {
+ Create.Index("IX_umbracoUserLogins_Index").OnTable("umbracoUserLogins").OnColumn("contextID").Ascending().WithOptions().Clustered();
+ }
}
public override void Down()
{
- throw new NotImplementedException();
- }
- }
-
- [Migration("6.2.0", 2, GlobalSettings.UmbracoMigrationName)]
- public class ChangePasswordColumn : MigrationBase
- {
- public override void Up()
- {
- //up to 500 chars
- Alter.Table("umbracoUser").AlterColumn("userPassword").AsString(500).NotNullable();
- }
-
- public override void Down()
- {
- throw new NotImplementedException();
+ Delete.Index("IX_umbracoNodeTrashed").OnTable("umbracoNode");
+ Delete.Index("IX_cmsContentVersion_ContentId").OnTable("cmsContentVersion");
+ Delete.Index("IX_cmsDocument_published").OnTable("cmsDocument");
+ Delete.Index("IX_cmsDocument_newest").OnTable("cmsDocument");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs
new file mode 100644
index 0000000000..a972b7eeb0
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs
@@ -0,0 +1,90 @@
+using System.Linq;
+using System.Web.UI;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
+{
+ //see: http://issues.umbraco.org/issue/U4-4430
+ [Migration("7.1.0", 0, GlobalSettings.UmbracoMigrationName)]
+ [Migration("6.2.0", 0, GlobalSettings.UmbracoMigrationName)]
+ public class AssignMissingPrimaryForMySqlKeys : MigrationBase
+ {
+ public override void Up()
+ {
+ if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
+ {
+ var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
+
+ //This should be 2 because this table has 2 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentType2ContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsContentType2ContentType")
+ .OnTable("cmsContentType2ContentType")
+ .Columns(new[] {"parentContentTypeId", "childContentTypeId"});
+ }
+
+ //This should be 2 because this table has 2 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentTypeAllowedContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsContentTypeAllowedContentType")
+ .OnTable("cmsContentTypeAllowedContentType")
+ .Columns(new[] { "Id", "AllowedId" });
+ }
+
+ //This should be 2 because this table has 2 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsDocumentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsDocumentType")
+ .OnTable("cmsDocumentType")
+ .Columns(new[] { "contentTypeNodeId", "templateNodeId" });
+ }
+
+ //This should be 2 because this table has 2 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsMember2MemberGroup") && x.Item3.InvariantEquals("PRIMARY")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsMember2MemberGroup")
+ .OnTable("cmsMember2MemberGroup")
+ .Columns(new[] { "Member", "MemberGroup" });
+ }
+
+ //This should be 2 because this table has 2 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsPreviewXml") && x.Item3.InvariantEquals("PRIMARY")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsContentPreviewXml")
+ .OnTable("cmsPreviewXml")
+ .Columns(new[] { "nodeId", "versionId" });
+ }
+
+ //This should be 2 because this table has 2 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("umbracoUser2app") && x.Item3.InvariantEquals("PRIMARY")) == 0)
+ {
+ Create.PrimaryKey("PK_user2app")
+ .OnTable("umbracoUser2app")
+ .Columns(new[] { "user", "app" });
+ }
+
+ //This should be 2 because this table has 3 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("umbracoUser2NodeNotify") && x.Item3.InvariantEquals("PRIMARY")) == 0)
+ {
+ Create.PrimaryKey("PK_umbracoUser2NodeNotify")
+ .OnTable("umbracoUser2NodeNotify")
+ .Columns(new[] { "userId", "nodeId", "action" });
+ }
+
+ //This should be 2 because this table has 3 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("umbracoUser2NodePermission") && x.Item3.InvariantEquals("PRIMARY")) == 0)
+ {
+ Create.PrimaryKey("PK_umbracoUser2NodePermission")
+ .OnTable("umbracoUser2NodePermission")
+ .Columns(new[] { "userId", "nodeId", "permission" });
+ }
+ }
+ }
+
+ public override void Down()
+ {
+ //don't do anything, these keys should have always existed!
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs
new file mode 100644
index 0000000000..437a188013
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs
@@ -0,0 +1,22 @@
+using System;
+using Umbraco.Core.Configuration;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
+{
+ [Migration("7.1.0", 2, GlobalSettings.UmbracoMigrationName)]
+ [Migration("6.2.0", 2, GlobalSettings.UmbracoMigrationName)]
+ public class ChangePasswordColumn : MigrationBase
+ {
+ public override void Up()
+ {
+ //up to 500 chars
+ Alter.Table("umbracoUser").AlterColumn("userPassword").AsString(500).NotNullable();
+ }
+
+ public override void Down()
+ {
+ //back to 125 chars
+ Alter.Table("umbracoUser").AlterColumn("userPassword").AsString(125).NotNullable();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs
new file mode 100644
index 0000000000..555c0bcdf1
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
+{
+ [Migration("7.1.0", 4, GlobalSettings.UmbracoMigrationName)]
+ [Migration("6.2.0", 4, GlobalSettings.UmbracoMigrationName)]
+ public class UpdateToNewMemberPropertyAliases : MigrationBase
+ {
+ public override void Up()
+ {
+ Execute.Code(Update);
+ }
+
+ internal static string Update(Database database)
+ {
+ if (database != null)
+ {
+ var aliasMap = new Dictionary
+ {
+ {"umbracoPasswordRetrievalQuestionPropertyTypeAlias", Constants.Conventions.Member.PasswordQuestion},
+ {"umbracoPasswordRetrievalAnswerPropertyTypeAlias", Constants.Conventions.Member.PasswordAnswer},
+ {"umbracoCommentPropertyTypeAlias", Constants.Conventions.Member.Comments},
+ {"umbracoApprovePropertyTypeAlias", Constants.Conventions.Member.IsApproved},
+ {"umbracoLockPropertyTypeAlias", Constants.Conventions.Member.IsLockedOut},
+ {"umbracoLastLoginPropertyTypeAlias", Constants.Conventions.Member.LastLoginDate},
+ {"umbracoMemberLastPasswordChange", Constants.Conventions.Member.LastPasswordChangeDate},
+ {"umbracoMemberLastLockout", Constants.Conventions.Member.LastLockoutDate},
+ {"umbracoFailedPasswordAttemptsPropertyTypeAlias", Constants.Conventions.Member.FailedPasswordAttempts}
+ };
+
+
+ //This query is structured to work with MySql, SQLCE and SqlServer:
+ // http://issues.umbraco.org/issue/U4-3876
+
+ const string propertyTypeUpdateSql = @"UPDATE cmsPropertyType
+SET Alias = @newAlias
+WHERE Alias = @oldAlias AND contentTypeId IN (
+SELECT nodeId FROM (SELECT DISTINCT cmsContentType.nodeId FROM cmsPropertyType
+INNER JOIN cmsContentType ON cmsPropertyType.contentTypeId = cmsContentType.nodeId
+INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id
+WHERE umbracoNode.nodeObjectType = @objectType) x)";
+
+ const string xmlSelectSql = @"SELECT cmsContentXml.* FROM cmsContentXml
+INNER JOIN umbracoNode ON cmsContentXml.nodeId = umbracoNode.id
+WHERE umbracoNode.nodeObjectType = @objectType";
+
+ using (var trans = database.GetTransaction())
+ {
+ try
+ {
+
+ //Upate all of the property type aliases
+ foreach (var map in aliasMap)
+ {
+ database.Execute(propertyTypeUpdateSql, new { newAlias = map.Value, oldAlias = map.Key, objectType = Constants.ObjectTypes.MemberType });
+ }
+
+ //Update all of the XML
+ var items = database.Fetch(xmlSelectSql, new { objectType = Constants.ObjectTypes.Member });
+ foreach (var item in items)
+ {
+ foreach (var map in aliasMap)
+ {
+ item.Xml = item.Xml.Replace("<" + map.Key + ">", "<" + map.Value + ">");
+ item.Xml = item.Xml.Replace("" + map.Key + ">", "" + map.Value + ">");
+ }
+ database.Update(item);
+ }
+
+ trans.Complete();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.Error("Exception was thrown when trying to upgrade old member aliases to the new ones", ex);
+ throw;
+ }
+ }
+
+
+ }
+ return string.Empty;
+ }
+
+ public override void Down()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
index 7ae81aaa16..6d479b9eb1 100644
--- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
+++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
@@ -21,14 +21,18 @@ namespace Umbraco.Core.Persistence
///
/// This will escape single @ symbols for peta poco values so it doesn't think it's a parameter
///
- ///
///
///
- public static string EscapeAtSymbols(this Database db, string value)
+ public static string EscapeAtSymbols(string value)
{
- //this fancy regex will only match a single @ not a double, etc...
- var regex = new Regex("(?(this Database db)
@@ -53,11 +57,20 @@ namespace Umbraco.Core.Persistence
using (var tr = db.GetTransaction())
{
- db.BulkInsertRecords(collection, tr);
+ db.BulkInsertRecords(collection, tr, true);
}
}
- public static void BulkInsertRecords(this Database db, IEnumerable collection, Transaction tr)
+ ///
+ /// Performs the bulk insertion in the context of a current transaction with an optional parameter to complete the transaction
+ /// when finished
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void BulkInsertRecords(this Database db, IEnumerable collection, Transaction tr, bool commitTrans = false)
{
//don't do anything if there are no records.
if (collection.Any() == false)
@@ -91,11 +104,17 @@ namespace Umbraco.Core.Persistence
}
}
- tr.Complete();
+ if (commitTrans)
+ {
+ tr.Complete();
+ }
}
catch
{
- tr.Dispose();
+ if (commitTrans)
+ {
+ tr.Dispose();
+ }
throw;
}
}
@@ -336,6 +355,14 @@ namespace Umbraco.Core.Persistence
creation.UninstallDatabaseSchema();
}
+ internal static void CreateDatabaseSchemaDo(this Database db, bool guardConfiguration)
+ {
+ if (guardConfiguration && ApplicationContext.Current.IsConfigured)
+ throw new Exception("Umbraco is already configured!");
+
+ CreateDatabaseSchemaDo(db);
+ }
+
internal static void CreateDatabaseSchemaDo(this Database db)
{
NewTable += PetaPocoExtensions_NewTable;
diff --git a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs
index 378ade59dd..4e1a3de28f 100644
--- a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs
+++ b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs
@@ -37,7 +37,8 @@ namespace Umbraco.Core.Persistence
var tableNameAttribute = type.FirstAttribute();
string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value;
- var syntax = string.Format("{0}.{1}",
+ //need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177
+ var syntax = string.Format("({0}.{1})",
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName),
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(columnName));
diff --git a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
index 44570adcd4..1a087ad8d7 100644
--- a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
+++ b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
@@ -1,5 +1,6 @@
using System;
using System.Globalization;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Querying
{
@@ -8,6 +9,41 @@ namespace Umbraco.Core.Persistence.Querying
///
internal class BaseExpressionHelper
{
+ protected string HandleStringComparison(string col, string val, string verb, TextColumnType columnType)
+ {
+ switch (verb)
+ {
+ case "SqlWildcard":
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, RemoveQuote(val), columnType);
+ case "Equals":
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, RemoveQuote(val), columnType);
+ case "StartsWith":
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnStartsWithComparison(col, RemoveQuote(val), columnType);
+ case "EndsWith":
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEndsWithComparison(col, RemoveQuote(val), columnType);
+ case "Contains":
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnContainsComparison(col, RemoveQuote(val), columnType);
+ case "InvariantEquals":
+ case "SqlEquals":
+ //recurse
+ return HandleStringComparison(col, val, "Equals", columnType);
+ case "InvariantStartsWith":
+ case "SqlStartsWith":
+ //recurse
+ return HandleStringComparison(col, val, "StartsWith", columnType);
+ case "InvariantEndsWith":
+ case "SqlEndsWith":
+ //recurse
+ return HandleStringComparison(col, val, "EndsWith", columnType);
+ case "InvariantContains":
+ case "SqlContains":
+ //recurse
+ return HandleStringComparison(col, val, "Contains", columnType);
+ default:
+ throw new ArgumentOutOfRangeException("verb");
+ }
+ }
+
public virtual string GetQuotedValue(object value, Type fieldType, Func escapeCallback = null, Func shouldQuoteCallback = null)
{
if (value == null) return "NULL";
@@ -25,7 +61,7 @@ namespace Umbraco.Core.Persistence.Querying
{
//if (TypeSerializer.CanCreateFromString(fieldType))
//{
- // return "'" + EscapeParam(TypeSerializer.SerializeToString(value)) + "'";
+ // return "'" + escapeCallback(TypeSerializer.SerializeToString(value)) + "'";
//}
throw new NotSupportedException(
@@ -46,33 +82,24 @@ namespace Umbraco.Core.Persistence.Querying
if (fieldType == typeof(DateTime))
{
- return "'" + EscapeParam(((DateTime)value).ToIsoString()) + "'";
+ return "'" + escapeCallback(((DateTime)value).ToIsoString()) + "'";
}
if (fieldType == typeof(bool))
return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
- return ShouldQuoteValue(fieldType)
- ? "'" + EscapeParam(value) + "'"
+ return shouldQuoteCallback(fieldType)
+ ? "'" + escapeCallback(value) + "'"
: value.ToString();
}
public virtual string EscapeParam(object paramValue)
{
- return paramValue.ToString().Replace("'", "''");
+ return paramValue == null
+ ? string.Empty
+ : SqlSyntaxContext.SqlSyntaxProvider.EscapeString(paramValue.ToString());
}
-
- public virtual string EscapeAtArgument(string exp)
- {
- /*if (exp.StartsWith("@"))
- return string.Concat("@", exp);*/
-
- if (exp.Contains("@"))
- return exp.Replace("@", "@@");
-
- return exp;
- }
-
+
public virtual bool ShouldQuoteValue(Type fieldType)
{
return true;
diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs
index 738cc8a23c..e3ff272cee 100644
--- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs
+++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs
@@ -2,9 +2,11 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
+using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Umbraco.Core.Persistence.Mappers;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Querying
{
@@ -203,13 +205,12 @@ namespace Umbraco.Core.Persistence.Querying
{
if (c.Value == null)
return "null";
- else if (c.Value.GetType() == typeof(bool))
+ if (c.Value is bool)
{
object o = GetQuotedValue(c.Value, c.Value.GetType());
return string.Format("({0}={1})", GetQuotedTrueValue(), o);
}
- else
- return GetQuotedValue(c.Value, c.Value.GetType());
+ return GetQuotedValue(c.Value, c.Value.GetType());
}
protected virtual string VisitUnary(UnaryExpression u)
@@ -245,12 +246,31 @@ namespace Umbraco.Core.Persistence.Querying
return string.Format("upper({0})", r);
case "ToLower":
return string.Format("lower({0})", r);
+ case "SqlWildcard":
case "StartsWith":
- return string.Format("upper({0}) like '{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString().ToUpper())));
case "EndsWith":
- return string.Format("upper({0}) like '%{1}'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper()));
case "Contains":
- return string.Format("{0} like '%{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper()));
+ case "Equals":
+ case "SqlStartsWith":
+ case "SqlEndsWith":
+ case "SqlContains":
+ case "SqlEquals":
+ case "InvariantStartsWith":
+ case "InvariantEndsWith":
+ case "InvariantContains":
+ case "InvariantEquals":
+ //default
+ var colType = TextColumnType.NVarchar;
+ //then check if this arg has been passed in
+ if (m.Arguments.Count > 1)
+ {
+ var colTypeArg = m.Arguments.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType));
+ if (colTypeArg != null)
+ {
+ colType = (TextColumnType) ((ConstantExpression) colTypeArg).Value;
+ }
+ }
+ return HandleStringComparison(r.ToString(), args[0].ToString(), m.Method.Name, colType);
case "Substring":
var startIndex = Int32.Parse(args[0].ToString()) + 1;
if (args.Count == 2)
diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs
index 555b87f6e4..9a27cc8183 100644
--- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs
+++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs
@@ -243,18 +243,41 @@ namespace Umbraco.Core.Persistence.Querying
args.RemoveAt(0);
}
+ //TODO: We should probably add the same logic we've done for ModelToSqlExpressionHelper with checking for:
+ // InvariantStartsWith, InvariantEndsWith, SqlWildcard, etc...
+ // since we should be able to easily handle that with the Poco objects too.
+
switch (m.Method.Name)
{
case "ToUpper":
return string.Format("upper({0})", r);
case "ToLower":
return string.Format("lower({0})", r);
+ case "SqlWildcard":
case "StartsWith":
- return string.Format("upper({0}) like '{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString().ToUpper())));
case "EndsWith":
- return string.Format("upper({0}) like '%{1}'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper()));
case "Contains":
- return string.Format("upper({0}) like '%{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper()));
+ case "Equals":
+ case "SqlStartsWith":
+ case "SqlEndsWith":
+ case "SqlContains":
+ case "SqlEquals":
+ case "InvariantStartsWith":
+ case "InvariantEndsWith":
+ case "InvariantContains":
+ case "InvariantEquals":
+ //default
+ var colType = TextColumnType.NVarchar;
+ //then check if this arg has been passed in
+ if (m.Arguments.Count > 1)
+ {
+ var colTypeArg = m.Arguments.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType));
+ if (colTypeArg != null)
+ {
+ colType = (TextColumnType)((ConstantExpression)colTypeArg).Value;
+ }
+ }
+ return HandleStringComparison(r.ToString(), args[0].ToString(), m.Method.Name, colType);
case "Substring":
var startIndex = Int32.Parse(args[0].ToString()) + 1;
if (args.Count == 2)
diff --git a/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs b/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs
new file mode 100644
index 0000000000..cbecc0a591
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Text.RegularExpressions;
+
+namespace Umbraco.Core.Persistence.Querying
+{
+ ///
+ /// String extension methods used specifically to translate into SQL
+ ///
+ internal static class SqlStringExtensions
+ {
+ public static bool SqlWildcard(this string str, string txt, TextColumnType columnType)
+ {
+ var wildcardmatch = new Regex("^" + Regex.Escape(txt).
+ //deal with any wildcard chars %
+ Replace(@"\%", ".*") + "$");
+
+ return wildcardmatch.IsMatch(str);
+ }
+
+ public static bool SqlContains(this string str, string txt, TextColumnType columnType)
+ {
+ return str.InvariantContains(txt);
+ }
+
+ public static bool SqlEquals(this string str, string txt, TextColumnType columnType)
+ {
+ return str.InvariantEquals(txt);
+ }
+
+ public static bool SqlStartsWith(this string str, string txt, TextColumnType columnType)
+ {
+ return str.InvariantStartsWith(txt);
+ }
+
+ public static bool SqlEndsWith(this string str, string txt, TextColumnType columnType)
+ {
+ return str.InvariantEndsWith(txt);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs b/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs
new file mode 100644
index 0000000000..f45efb412a
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs
@@ -0,0 +1,15 @@
+namespace Umbraco.Core.Persistence.Querying
+{
+ ///
+ /// Determines how to match a string property value
+ ///
+ public enum StringPropertyMatchType
+ {
+ Exact,
+ Contains,
+ StartsWith,
+ EndsWith,
+ //Deals with % as wildcard chars in a string
+ Wildcard
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Querying/TextColumnType.cs b/src/Umbraco.Core/Persistence/Querying/TextColumnType.cs
new file mode 100644
index 0000000000..f33022fcbe
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Querying/TextColumnType.cs
@@ -0,0 +1,8 @@
+namespace Umbraco.Core.Persistence.Querying
+{
+ public enum TextColumnType
+ {
+ NVarchar,
+ NText
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs b/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs
new file mode 100644
index 0000000000..f0c70a069d
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs
@@ -0,0 +1,14 @@
+namespace Umbraco.Core.Persistence.Querying
+{
+ ///
+ /// Determine how to match a number or data value
+ ///
+ public enum ValuePropertyMatchType
+ {
+ Exact,
+ GreaterThan,
+ LessThan,
+ GreaterThanOrEqualTo,
+ LessThanOrEqualTo
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Relators/PropertyDataRelator.cs b/src/Umbraco.Core/Persistence/Relators/PropertyDataRelator.cs
index 31fd430118..79486b6e5b 100644
--- a/src/Umbraco.Core/Persistence/Relators/PropertyDataRelator.cs
+++ b/src/Umbraco.Core/Persistence/Relators/PropertyDataRelator.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Linq;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Relators
@@ -20,8 +21,12 @@ namespace Umbraco.Core.Persistence.Relators
// Is this the same MemberReadOnlyDto as the current one we're processing
if (Current != null && Current.UniqueId == a.UniqueId)
{
- // Yes, just add this PropertyDataReadOnlyDto to the current MemberReadOnlyDto's collection
- Current.Properties.Add(p);
+ //This property may already be added so we need to check for that
+ if (Current.Properties.Any(x => x.Id == p.Id) == false)
+ {
+ // Yes, just add this PropertyDataReadOnlyDto to the current MemberReadOnlyDto's collection
+ Current.Properties.Add(p);
+ }
// Return null to indicate we're not done with this MemberReadOnlyDto yet
return null;
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index fe05d8d3b8..3a9e06616b 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -24,8 +24,9 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly IContentTypeRepository _contentTypeRepository;
private readonly ITemplateRepository _templateRepository;
private readonly ITagsRepository _tagRepository;
+ private readonly CacheHelper _cacheHelper;
- public ContentRepository(IDatabaseUnitOfWork work, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository)
+ public ContentRepository(IDatabaseUnitOfWork work, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository, CacheHelper cacheHelper)
: base(work)
{
if (contentTypeRepository == null) throw new ArgumentNullException("contentTypeRepository");
@@ -34,11 +35,12 @@ namespace Umbraco.Core.Persistence.Repositories
_contentTypeRepository = contentTypeRepository;
_templateRepository = templateRepository;
_tagRepository = tagRepository;
+ _cacheHelper = cacheHelper;
EnsureUniqueNaming = true;
}
- public ContentRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository)
+ public ContentRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository, CacheHelper cacheHelper)
: base(work, cache)
{
if (contentTypeRepository == null) throw new ArgumentNullException("contentTypeRepository");
@@ -47,6 +49,7 @@ namespace Umbraco.Core.Persistence.Repositories
_contentTypeRepository = contentTypeRepository;
_templateRepository = templateRepository;
_tagRepository = tagRepository;
+ _cacheHelper = cacheHelper;
EnsureUniqueNaming = true;
}
@@ -139,6 +142,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var list = new List
{
+ "DELETE FROM cmsTask WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
"DELETE FROM umbracoRelation WHERE parentId = @Id",
@@ -273,8 +277,8 @@ namespace Umbraco.Core.Persistence.Repositories
//Assign the same permissions to it as the parent node
- // http://issues.umbraco.org/issue/U4-2161
- var permissionsRepo = new PermissionRepository(UnitOfWork);
+ // http://issues.umbraco.org/issue/U4-2161
+ var permissionsRepo = new PermissionRepository(UnitOfWork, _cacheHelper);
var parentPermissions = permissionsRepo.GetPermissionsForEntity(entity.ParentId).ToArray();
//if there are parent permissions then assign them, otherwise leave null and permissions will become the
// user's default permissions.
@@ -283,9 +287,9 @@ namespace Umbraco.Core.Persistence.Repositories
var userPermissions = (
from perm in parentPermissions
from p in perm.AssignedPermissions
- select new Tuple(perm.UserId, p)).ToList();
-
- permissionsRepo.AssignEntityPermissions(entity, userPermissions);
+ select new Tuple(perm.UserId, p)).ToList();
+
+ permissionsRepo.ReplaceEntityPermissions(entity, userPermissions);
//flag the entity's permissions changed flag so we can track those changes.
//Currently only used for the cache refreshers to detect if we should refresh all user permissions cache.
((Content) entity).PermissionsChanged = true;
@@ -429,7 +433,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
- //In order to update the ContentVersion we need to retreive its primary key id
+ //In order to update the ContentVersion we need to retrieve its primary key id
var contentVerDto = Database.SingleOrDefault("WHERE VersionId = @Version", new { Version = entity.Version });
contentVersionDto.Id = contentVerDto.Id;
@@ -555,15 +559,21 @@ namespace Umbraco.Core.Persistence.Repositories
return GetByVersion(dto.ContentVersionDto.VersionId);
}
- public void AssignEntityPermissions(IContent entity, char permission, IEnumerable userIds)
+ ///
+ /// Assigns a single permission to the current content item for the specified user ids
+ ///
+ ///
+ ///
+ ///
+ public void AssignEntityPermission(IContent entity, char permission, IEnumerable userIds)
{
- var repo = new PermissionRepository(UnitOfWork);
- repo.AssignEntityPermissions(entity, permission, userIds);
+ var repo = new PermissionRepository(UnitOfWork, _cacheHelper);
+ repo.AssignEntityPermission(entity, permission, userIds);
}
public IEnumerable GetPermissionsForEntity(int entityId)
{
- var repo = new PermissionRepository(UnitOfWork);
+ var repo = new PermissionRepository(UnitOfWork, _cacheHelper);
return repo.GetPermissionsForEntity(entityId);
}
@@ -596,33 +606,6 @@ namespace Umbraco.Core.Persistence.Repositories
return content;
}
- private PropertyCollection GetPropertyCollection(int id, Guid versionId, IContentType contentType, DateTime createDate, DateTime updateDate)
- {
- var sql = new Sql();
- sql.Select("*")
- .From()
- .InnerJoin()
- .On(left => left.PropertyTypeId, right => right.Id)
- .Where(x => x.NodeId == id)
- .Where(x => x.VersionId == versionId);
-
- var propertyDataDtos = Database.Fetch(sql);
- var propertyFactory = new PropertyFactory(contentType, versionId, id, createDate, updateDate);
- var properties = propertyFactory.BuildEntity(propertyDataDtos);
-
- var newProperties = properties.Where(x => x.HasIdentity == false);
- foreach (var property in newProperties)
- {
- var propertyDataDto = new PropertyDataDto{ NodeId = id, PropertyTypeId = property.PropertyTypeId, VersionId = versionId };
- int primaryKey = Convert.ToInt32(Database.Insert(propertyDataDto));
-
- property.Version = versionId;
- property.Id = primaryKey;
- }
-
- return new PropertyCollection(properties);
- }
-
private string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0)
{
if (EnsureUniqueNaming == false)
@@ -655,4 +638,4 @@ namespace Umbraco.Core.Persistence.Repositories
return currentName;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
index 6afcd8005f..b88b928e9c 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Data;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
+using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
@@ -10,6 +12,7 @@ using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Relators;
+using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
@@ -54,23 +57,25 @@ namespace Umbraco.Core.Persistence.Repositories
yield return dto.ContentTypeNodeId;
}
}
-
- ///
- /// We need to override this method to ensure that any content cache is cleared
- ///
- ///
- ///
- /// see: http://issues.umbraco.org/issue/U4-1963
- ///
- public override void PersistUpdatedItem(IEntity entity)
+
+ protected virtual PropertyType CreatePropertyType(string propertyEditorAlias, DataTypeDatabaseType dbType, string propertyTypeAlias)
{
- InMemoryCacheProvider.Current.Clear(typeof(IContent));
- RuntimeCacheProvider.Current.Clear(typeof(IContent));
- base.PersistUpdatedItem(entity);
+ return new PropertyType(propertyEditorAlias, dbType);
}
protected void PersistNewBaseContentType(ContentTypeDto dto, IContentTypeComposition entity)
{
+ //Cannot add a duplicate content type type
+ var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType
+INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id
+WHERE cmsContentType." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("alias") + @"= @alias
+AND umbracoNode.nodeObjectType = @objectType",
+ new { alias = entity.Alias, objectType = NodeObjectTypeId });
+ if (exists > 0)
+ {
+ throw new DuplicateNameException("An item with the alias " + entity.Alias + " already exists");
+ }
+
//Logic for setting Path, Level and SortOrder
var parent = Database.First("WHERE id = @ParentId", new { ParentId = entity.ParentId });
int level = parent.Level + 1;
@@ -158,8 +163,7 @@ namespace Umbraco.Core.Persistence.Repositories
//If the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias
if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int))
{
- var datatype = Database.FirstOrDefault("WHERE propertyEditorAlias = @alias", new { alias = propertyType.PropertyEditorAlias });
- propertyType.DataTypeDefinitionId = datatype.DataTypeId;
+ AssignDataTypeFromPropertyEditor(propertyType);
}
var propertyTypeDto = propertyFactory.BuildPropertyTypeDto(tabId, propertyType);
int typePrimaryKey = Convert.ToInt32(Database.Insert(propertyTypeDto));
@@ -174,6 +178,19 @@ namespace Umbraco.Core.Persistence.Repositories
protected void PersistUpdatedBaseContentType(ContentTypeDto dto, IContentTypeComposition entity)
{
+
+ //Cannot update to a duplicate alias
+ var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType
+INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id
+WHERE cmsContentType." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("alias") + @"= @alias
+AND umbracoNode.nodeObjectType = @objectType
+AND umbracoNode.id <> @id",
+ new { id = dto.NodeId, alias = entity.Alias, objectType = NodeObjectTypeId });
+ if (exists > 0)
+ {
+ throw new DuplicateNameException("An item with the alias " + entity.Alias + " already exists");
+ }
+
var propertyGroupFactory = new PropertyGroupFactory(entity.Id);
var nodeDto = dto.NodeDto;
@@ -316,9 +333,12 @@ namespace Umbraco.Core.Persistence.Repositories
//If the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias
if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int))
{
- var datatype = Database.FirstOrDefault("WHERE propertyEditorAlias = @alias", new { alias = propertyType.PropertyEditorAlias });
- propertyType.DataTypeDefinitionId = datatype.DataTypeId;
+ AssignDataTypeFromPropertyEditor(propertyType);
}
+
+ //validate the alias!
+ ValidateAlias(propertyType);
+
var propertyTypeDto = propertyGroupFactory.BuildPropertyTypeDto(tabId, propertyType);
int typePrimaryKey = propertyType.HasIdentity
? Database.Update(propertyTypeDto)
@@ -355,7 +375,7 @@ namespace Umbraco.Core.Persistence.Repositories
var dtos = Database.Fetch(new GroupPropertyTypeRelator().Map, sql);
- var propertyGroupFactory = new PropertyGroupFactory(id, createDate, updateDate);
+ var propertyGroupFactory = new PropertyGroupFactory(id, createDate, updateDate, CreatePropertyType);
var propertyGroups = propertyGroupFactory.BuildEntity(dtos);
return new PropertyGroupCollection(propertyGroups);
}
@@ -372,29 +392,88 @@ namespace Umbraco.Core.Persistence.Repositories
var dtos = Database.Fetch(sql);
//TODO Move this to a PropertyTypeFactory
- var list = (from dto in dtos
- where (dto.PropertyTypeGroupId > 0) == false
- select
- new PropertyType(dto.DataTypeDto.PropertyEditorAlias,
- dto.DataTypeDto.DbType.EnumParse(true))
- {
- Alias = dto.Alias,
- DataTypeDefinitionId = dto.DataTypeId,
- Description = dto.Description,
- Id = dto.Id,
- Name = dto.Name,
- HelpText = dto.HelpText,
- Mandatory = dto.Mandatory,
- SortOrder = dto.SortOrder,
- ValidationRegExp = dto.ValidationRegExp,
- CreateDate = createDate,
- UpdateDate = updateDate
- }).ToList();
-
+ var list = new List();
+ foreach (var dto in dtos.Where(x => (x.PropertyTypeGroupId > 0) == false))
+ {
+ var propType = CreatePropertyType(dto.DataTypeDto.PropertyEditorAlias, dto.DataTypeDto.DbType.EnumParse(true), dto.Alias);
+ propType.Alias = dto.Alias;
+ propType.DataTypeDefinitionId = dto.DataTypeId;
+ propType.Description = dto.Description;
+ propType.Id = dto.Id;
+ propType.Name = dto.Name;
+ propType.HelpText = dto.HelpText;
+ propType.Mandatory = dto.Mandatory;
+ propType.SortOrder = dto.SortOrder;
+ propType.ValidationRegExp = dto.ValidationRegExp;
+ propType.CreateDate = createDate;
+ propType.UpdateDate = updateDate;
+ list.Add(propType);
+ }
//Reset dirty properties
Parallel.ForEach(list, currentFile => currentFile.ResetDirtyProperties(false));
return new PropertyTypeCollection(list);
}
+
+ protected void ValidateAlias(PropertyType pt)
+ {
+ Mandate.That(string.IsNullOrEmpty(pt.Alias) == false,
+ () =>
+ {
+ var message =
+ string.Format(
+ "{0} '{1}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias.",
+ "Property Type",
+ pt.Name);
+ var exception = new InvalidOperationException(message);
+
+ LogHelper.Error>(message, exception);
+ throw exception;
+ });
+ }
+
+ protected void ValidateAlias(TEntity entity)
+ {
+ Mandate.That(string.IsNullOrEmpty(entity.Alias) == false,
+ () =>
+ {
+ var message =
+ string.Format(
+ "{0} '{1}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias.",
+ typeof(TEntity).Name,
+ entity.Name);
+ var exception = new InvalidOperationException(message);
+
+ LogHelper.Error>(message, exception);
+ throw exception;
+ });
+ }
+
+ ///
+ /// Try to set the data type id based on its ControlId
+ ///
+ ///
+ private void AssignDataTypeFromPropertyEditor(PropertyType propertyType)
+ {
+ //we cannot try to assign a data type of it's empty
+ if (propertyType.PropertyEditorAlias.IsNullOrWhiteSpace() == false)
+ {
+ var sql = new Sql()
+ .Select("*")
+ .From()
+ .Where("propertyEditorAlias = @propertyEditorAlias", new { propertyEditorAlias = propertyType.PropertyEditorAlias })
+ .OrderBy(typeDto => typeDto.DataTypeId);
+ var datatype = Database.FirstOrDefault(sql);
+ //we cannot assign a data type if one was not found
+ if (datatype != null)
+ {
+ propertyType.DataTypeDefinitionId = datatype.DataTypeId;
+ }
+ else
+ {
+ LogHelper.Warn>("Could not assign a data type for the property type " + propertyType.Alias + " since no data type was found with a property editor " + propertyType.PropertyEditorAlias);
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs
index 3bf810cd13..ec5f810152 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs
@@ -229,18 +229,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override void PersistUpdatedItem(IContentType entity)
{
- Mandate.That(string.IsNullOrEmpty(entity.Alias) == false,
- () =>
- {
- var message =
- string.Format(
- "ContentType '{0}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias.",
- entity.Name);
- var exception = new Exception(message);
-
- LogHelper.Error(message, exception);
- throw exception;
- });
+ ValidateAlias(entity);
//Updates Modified date
((ContentType)entity).UpdatingEntity();
diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs
index 7cd7069751..3798bb53d9 100644
--- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Data;
using System.Globalization;
using System.Linq;
using Umbraco.Core.Models;
@@ -8,6 +9,7 @@ using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
+using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
@@ -119,6 +121,15 @@ namespace Umbraco.Core.Persistence.Repositories
{
((DataTypeDefinition)entity).AddingEntity();
+ //Cannot add a duplicate data type
+ var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsDataType
+INNER JOIN umbracoNode ON cmsDataType.nodeId = umbracoNode.id
+WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + "= @name", new {name = entity.Name});
+ if (exists > 0)
+ {
+ throw new DuplicateNameException("A data type with the name " + entity.Name + " already exists");
+ }
+
var factory = new DataTypeDefinitionFactory(NodeObjectTypeId);
var dto = factory.BuildDto(entity);
@@ -154,6 +165,18 @@ namespace Umbraco.Core.Persistence.Repositories
protected override void PersistUpdatedItem(IDataTypeDefinition entity)
{
+
+ //Cannot change to a duplicate alias
+ var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsDataType
+INNER JOIN umbracoNode ON cmsDataType.nodeId = umbracoNode.id
+WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + @"= @name
+AND umbracoNode.id <> @id",
+ new { id = entity.Id, name = entity.Name });
+ if (exists > 0)
+ {
+ throw new DuplicateNameException("A data type with the name " + entity.Name + " already exists");
+ }
+
//Updates Modified date and Version Guid
((DataTypeDefinition)entity).UpdatingEntity();
diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs
index 0b456cda91..f98c0f1e71 100644
--- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs
@@ -50,6 +50,8 @@ namespace Umbraco.Core.Persistence.Repositories
foreach (var textDto in dto.LanguageTextDtos)
{
var language = _languageRepository.Get(textDto.LanguageId);
+ if (language == null)
+ continue;
var translationFactory = new DictionaryTranslationFactory(dto.UniqueId, language);
list.Add(translationFactory.BuildEntity(textDto));
}
@@ -180,7 +182,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
- translation.Id = Convert.ToInt32(Database.Insert(dto));
+ translation.Id = Convert.ToInt32(Database.Insert(textDto));
translation.Key = entity.Key;
}
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs
index d70aa9510c..cd36f75369 100644
--- a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs
@@ -33,6 +33,16 @@ namespace Umbraco.Core.Persistence.Repositories
get { return _fileSystem; }
}
+ internal virtual void AddFolder(string folderPath)
+ {
+ _work.RegisterAdded(new Folder(folderPath), this);
+ }
+
+ internal virtual void DeleteFolder(string folderPath)
+ {
+ _work.RegisterRemoved(new Folder(folderPath), this);
+ }
+
#region Implementation of IRepository
public virtual void AddOrUpdate(TEntity entity)
@@ -68,7 +78,16 @@ namespace Umbraco.Core.Persistence.Repositories
public void PersistNewItem(IEntity entity)
{
- PersistNewItem((TEntity)entity);
+ //special case for folder
+ var folder = entity as Folder;
+ if (folder != null)
+ {
+ PersistNewFolder(folder);
+ }
+ else
+ {
+ PersistNewItem((TEntity)entity);
+ }
}
public void PersistUpdatedItem(IEntity entity)
@@ -78,23 +97,46 @@ namespace Umbraco.Core.Persistence.Repositories
public void PersistDeletedItem(IEntity entity)
{
- PersistDeletedItem((TEntity)entity);
+ //special case for folder
+ var folder = entity as Folder;
+ if (folder != null)
+ {
+ PersistDeletedFolder(folder);
+ }
+ else
+ {
+ PersistDeletedItem((TEntity)entity);
+ }
}
#endregion
- #region Abstract IUnitOfWorkRepository Methods
+ internal virtual void PersistNewFolder(Folder entity)
+ {
+ _fileSystem.CreateFolder(entity.Path);
+ }
+ internal virtual void PersistDeletedFolder(Folder entity)
+ {
+ _fileSystem.DeleteDirectory(entity.Path);
+ }
+
+ #region Abstract IUnitOfWorkRepository Methods
+
protected virtual void PersistNewItem(TEntity entity)
{
- var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content));
- FileSystem.AddFile(entity.Name, stream, true);
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content)))
+ {
+ FileSystem.AddFile(entity.Path, stream, true);
+ }
}
protected virtual void PersistUpdatedItem(TEntity entity)
{
- var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content));
- FileSystem.AddFile(entity.Name, stream, true);
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content)))
+ {
+ FileSystem.AddFile(entity.Path, stream, true);
+ }
}
protected virtual void PersistDeletedItem(TEntity entity)
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs
index 06beebda9e..3a343febae 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs
@@ -23,8 +23,19 @@ namespace Umbraco.Core.Persistence.Repositories
/// An enumerable list of
IEnumerable GetByPublishedVersion(IQuery query);
- void AssignEntityPermissions(IContent entity, char permission, IEnumerable userIds);
+ ///
+ /// Assigns a single permission to the current content item for the specified user ids
+ ///
+ ///
+ ///
+ ///
+ void AssignEntityPermission(IContent entity, char permission, IEnumerable userIds);
+ ///
+ /// Gets the list of permissions for the content item
+ ///
+ ///
+ ///
IEnumerable GetPermissionsForEntity(int entityId);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberGroupRepository.cs
new file mode 100644
index 0000000000..2750457271
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberGroupRepository.cs
@@ -0,0 +1,43 @@
+using System.Collections.Generic;
+using Umbraco.Core.Models;
+
+namespace Umbraco.Core.Persistence.Repositories
+{
+ public interface IMemberGroupRepository : IRepositoryQueryable
+ {
+ ///
+ /// Gets a member group by it's name
+ ///
+ ///
+ ///
+ IMemberGroup GetByName(string name);
+
+ ///
+ /// Creates the new member group if it doesn't already exist
+ ///
+ ///
+ IMemberGroup CreateIfNotExists(string roleName);
+
+ ///
+ /// Returns the member groups for a given member
+ ///
+ ///
+ ///
+ IEnumerable GetMemberGroupsForMember(int memberId);
+
+ ///
+ /// Returns the member groups for a given member
+ ///
+ ///
+ ///
+ IEnumerable GetMemberGroupsForMember(string username);
+
+ void AssignRoles(string[] usernames, string[] roleNames);
+
+ void DissociateRoles(string[] usernames, string[] roleNames);
+
+ void AssignRoles(int[] memberIds, string[] roleNames);
+
+ void DissociateRoles(int[] memberIds, string[] roleNames);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs
index 17c697ecdd..f9c42511d9 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs
@@ -1,10 +1,23 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
- internal interface IMemberRepository : IRepositoryVersionable
+ public interface IMemberRepository : IRepositoryVersionable
{
+ ///
+ /// Finds members in a given role
+ ///
+ ///
+ ///
+ ///
+ ///
+ IEnumerable FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith);
+
///
/// Get all members in a specific group
///
@@ -19,5 +32,27 @@ namespace Umbraco.Core.Persistence.Repositories
///
bool Exists(string username);
+ ///
+ /// Gets the count of items based on a complex query
+ ///
+ ///
+ ///
+ int GetCountByQuery(IQuery query);
+
+ ///
+ /// Gets paged member results
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy);
+
+ IEnumerable GetPagedResultsByQuery(
+ Sql sql, int pageIndex, int pageSize, out int totalRecords,
+ Func, int[]> resolveIds);
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationRepository.cs
index de7ff3ba66..f6c7fce4a2 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationRepository.cs
@@ -4,6 +4,6 @@ namespace Umbraco.Core.Persistence.Repositories
{
public interface IRelationRepository : IRepositoryQueryable
{
-
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs
index 5fa35df140..d28f81bce0 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs
@@ -4,6 +4,6 @@ namespace Umbraco.Core.Persistence.Repositories
{
public interface IRelationTypeRepository : IRepositoryQueryable
{
-
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs
index 45bde95b3f..8c675a7d3a 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs
@@ -4,6 +4,6 @@ namespace Umbraco.Core.Persistence.Repositories
{
public interface IScriptRepository : IRepository
{
-
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs
index b64815188b..e40d996637 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs
@@ -1,27 +1,59 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
- internal interface IUserRepository : IRepositoryQueryable
+ public interface IUserRepository : IRepositoryQueryable
{
- //IProfile GetProfileById(int id);
- IProfile GetProfileByUserName(string username);
- IUser GetUserByUserName(string username);
-
+ ///
+ /// Gets the count of items based on a complex query
+ ///
+ ///
+ ///
+ int GetCountByQuery(IQuery query);
+
+ ///
+ /// Checks if a user with the username exists
+ ///
+ ///
+ ///
+ bool Exists(string username);
+
///
/// This is useful when an entire section is removed from config
///
///
IEnumerable GetUsersAssignedToSection(string sectionAlias);
+ ///
+ /// Gets paged member results
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy);
+
+
///
/// Gets the user permissions for the specified entities
///
///
///
///
- IEnumerable GetUserPermissionsForEntities(object userId, params int[] entityIds);
+ IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds);
+ ///
+ /// Replaces the same permission set for a single user to any number of entities
+ ///
+ ///
+ ///
+ ///
+ void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserTypeRepository.cs
index efa50f6641..43b6709aa7 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserTypeRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserTypeRepository.cs
@@ -2,7 +2,7 @@
namespace Umbraco.Core.Persistence.Repositories
{
- internal interface IUserTypeRepository : IRepositoryQueryable
+ public interface IUserTypeRepository : IRepositoryQueryable
{
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs
index d52a2549f4..0c891d512d 100644
--- a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
- var languageDto = Database.First(sql);
+ var languageDto = Database.FirstOrDefault(sql);
if (languageDto == null)
return null;
diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
index 1746e6b68a..27bd68c1f0 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
@@ -131,6 +131,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var list = new List
{
+ "DELETE FROM cmsTask WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
"DELETE FROM umbracoRelation WHERE parentId = @Id",
@@ -298,7 +299,7 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Update(newContentDto);
}
- //In order to update the ContentVersion we need to retreive its primary key id
+ //In order to update the ContentVersion we need to retrieve its primary key id
var contentVerDto = Database.SingleOrDefault("WHERE VersionId = @Version", new { Version = entity.Version });
dto.Id = contentVerDto.Id;
//Updates the current version - cmsContentVersion
@@ -370,33 +371,6 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
- private PropertyCollection GetPropertyCollection(int id, Guid versionId, IMediaType contentType, DateTime createDate, DateTime updateDate)
- {
- var sql = new Sql();
- sql.Select("*")
- .From()
- .InnerJoin()
- .On(left => left.PropertyTypeId, right => right.Id)
- .Where(x => x.NodeId == id)
- .Where(x => x.VersionId == versionId);
-
- var propertyDataDtos = Database.Fetch(sql);
- var propertyFactory = new PropertyFactory(contentType, versionId, id, createDate, updateDate);
- var properties = propertyFactory.BuildEntity(propertyDataDtos);
-
- var newProperties = properties.Where(x => x.HasIdentity == false);
- foreach (var property in newProperties)
- {
- var propertyDataDto = new PropertyDataDto { NodeId = id, PropertyTypeId = property.PropertyTypeId, VersionId = versionId };
- int primaryKey = Convert.ToInt32(Database.Insert(propertyDataDto));
-
- property.Version = versionId;
- property.Id = primaryKey;
- }
-
- return new PropertyCollection(properties);
- }
-
private string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0)
{
if (EnsureUniqueNaming == false)
@@ -429,4 +403,4 @@ namespace Umbraco.Core.Persistence.Repositories
return currentName;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs
index 515557c4ef..18ff7e8e4c 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
@@ -162,6 +163,8 @@ namespace Umbraco.Core.Persistence.Repositories
protected override void PersistUpdatedItem(IMediaType entity)
{
+ ValidateAlias(entity);
+
//Updates Modified date
((MediaType)entity).UpdatingEntity();
diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
index a93355ceb3..8e051e77e5 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
@@ -1,7 +1,384 @@
-namespace Umbraco.Core.Persistence.Repositories
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core.Events;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.Caching;
+using Umbraco.Core.Persistence.Factories;
+using Umbraco.Core.Persistence.Querying;
+using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Core.Persistence.UnitOfWork;
+using Umbraco.Core.Services;
+using Umbraco.Core.Cache;
+
+namespace Umbraco.Core.Persistence.Repositories
{
- public class MemberGroupRepository
+
+
+ internal class MemberGroupRepository : PetaPocoRepositoryBase, IMemberGroupRepository
{
-
+ private readonly CacheHelper _cacheHelper;
+
+ public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper)
+ : base(work)
+ {
+ if (cacheHelper == null) throw new ArgumentNullException("cacheHelper");
+ _cacheHelper = cacheHelper;
+ }
+
+ public MemberGroupRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, CacheHelper cacheHelper)
+ : base(work, cache)
+ {
+ if (cacheHelper == null) throw new ArgumentNullException("cacheHelper");
+ _cacheHelper = cacheHelper;
+ }
+
+ private readonly MemberGroupFactory _modelFactory = new MemberGroupFactory();
+
+ protected override IMemberGroup PerformGet(int id)
+ {
+ var sql = GetBaseQuery(false);
+ sql.Where(GetBaseWhereClause(), new { Id = id });
+
+ var dto = Database.Fetch(sql).FirstOrDefault();
+
+ return dto == null ? null : _modelFactory.BuildEntity(dto);
+ }
+
+ protected override IEnumerable PerformGetAll(params int[] ids)
+ {
+ if (ids.Any())
+ {
+ var sql = new Sql()
+ .Select("*")
+ .From()
+ .Where(dto => dto.NodeObjectType == NodeObjectTypeId)
+ .Where("umbracoNode.id in (@ids)", new { ids = ids });
+ return Database.Fetch(sql)
+ .Select(x => _modelFactory.BuildEntity(x));
+ }
+ else
+ {
+ var sql = new Sql()
+ .From()
+ .Where(dto => dto.NodeObjectType == NodeObjectTypeId);
+ return Database.Fetch(sql)
+ .Select(x => _modelFactory.BuildEntity(x));
+ }
+ }
+
+ protected override IEnumerable PerformGetByQuery(IQuery query)
+ {
+ var sqlClause = GetBaseQuery(false);
+ var translator = new SqlTranslator(sqlClause, query);
+ var sql = translator.Translate();
+
+ return Database.Fetch(sql)
+ .Select(x => _modelFactory.BuildEntity(x));
+ }
+
+ protected override Sql GetBaseQuery(bool isCount)
+ {
+ var sql = new Sql();
+ sql.Select(isCount ? "COUNT(*)" : "*")
+ .From()
+ .Where(x => x.NodeObjectType == NodeObjectTypeId);
+ return sql;
+ }
+
+ protected override string GetBaseWhereClause()
+ {
+ return "umbracoNode.id = @Id";
+ }
+
+ protected override IEnumerable GetDeleteClauses()
+ {
+ var list = new[]
+ {
+ "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
+ "DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
+ "DELETE FROM umbracoRelation WHERE parentId = @Id",
+ "DELETE FROM umbracoRelation WHERE childId = @Id",
+ "DELETE FROM cmsTagRelationship WHERE nodeId = @Id",
+ "DELETE FROM cmsMember2MemberGroup WHERE MemberGroup = @Id",
+ "DELETE FROM umbracoNode WHERE id = @Id"
+ };
+ return list;
+ }
+
+ protected override Guid NodeObjectTypeId
+ {
+ get { return new Guid(Constants.ObjectTypes.MemberGroup); }
+ }
+
+ protected override void PersistNewItem(IMemberGroup entity)
+ {
+ //Save to db
+ var group = (MemberGroup)entity;
+ group.AddingEntity();
+ var dto = _modelFactory.BuildDto(group);
+ var o = Database.IsNew(dto) ? Convert.ToInt32(Database.Insert(dto)) : Database.Update(dto);
+
+ //Update with new correct path and id
+ dto.Path = string.Concat("-1,", dto.NodeId);
+ Database.Update(dto);
+ //assign to entity
+ group.Id = o;
+ group.ResetDirtyProperties();
+ }
+
+ protected override void PersistUpdatedItem(IMemberGroup entity)
+ {
+ var dto = _modelFactory.BuildDto(entity);
+
+ Database.Update(dto);
+
+ ((ICanBeDirty)entity).ResetDirtyProperties();
+ }
+
+ public IMemberGroup GetByName(string name)
+ {
+ return _cacheHelper.RuntimeCache.GetCacheItem(
+ string.Format("{0}.{1}", typeof (IMemberGroup).FullName, name),
+ () =>
+ {
+ var qry = new Query().Where(group => group.Name.Equals(name));
+ var result = GetByQuery(qry);
+ return result.FirstOrDefault();
+ },
+ //cache for 5 mins since that is the default in the RuntimeCacheProvider
+ TimeSpan.FromMinutes(5),
+ //sliding is true
+ true);
+ }
+
+ public IMemberGroup CreateIfNotExists(string roleName)
+ {
+ using (var transaction = Database.GetTransaction())
+ {
+ var qry = new Query().Where(group => group.Name.Equals(roleName));
+ var result = GetByQuery(qry);
+
+ if (result.Any()) return null;
+
+ var grp = new MemberGroup
+ {
+ Name = roleName
+ };
+ PersistNewItem(grp);
+
+ if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(grp), this))
+ {
+ return null;
+ }
+
+ transaction.Complete();
+
+ SavedMemberGroup.RaiseEvent(new SaveEventArgs(grp), this);
+
+ return grp;
+ }
+ }
+
+ public IEnumerable GetMemberGroupsForMember(int memberId)
+ {
+ var sql = new Sql();
+ sql.Select("umbracoNode.*")
+ .From()
+ .InnerJoin()
+ .On(dto => dto.NodeId, dto => dto.MemberGroup)
+ .Where(x => x.NodeObjectType == NodeObjectTypeId)
+ .Where(x => x.Member == memberId);
+
+ return Database.Fetch(sql)
+ .DistinctBy(dto => dto.NodeId)
+ .Select(x => _modelFactory.BuildEntity(x));
+ }
+
+ public IEnumerable GetMemberGroupsForMember(string username)
+ {
+ //find the member by username
+ var memberSql = new Sql();
+ var memberObjectType = new Guid(Constants.ObjectTypes.Member);
+ var escapedUsername = PetaPocoExtensions.EscapeAtSymbols(username);
+ memberSql.Select("umbracoNode.id")
+ .From()
+ .InnerJoin()
+ .On(dto => dto.NodeId, dto => dto.NodeId)
+ .Where(x => x.NodeObjectType == memberObjectType)
+ .Where(x => x.LoginName == escapedUsername);
+ var memberIdUsername = Database.Fetch(memberSql).FirstOrDefault();
+ if (memberIdUsername.HasValue == false)
+ {
+ return Enumerable.Empty();
+ }
+
+ var sql = new Sql();
+ sql.Select("umbracoNode.*")
+ .From()
+ .InnerJoin()
+ .On(dto => dto.NodeId, dto => dto.MemberGroup)
+ .Where(x => x.NodeObjectType == NodeObjectTypeId)
+ .Where(x => x.Member == memberIdUsername.Value);
+
+ return Database.Fetch(sql)
+ .DistinctBy(dto => dto.NodeId)
+ .Select(x => _modelFactory.BuildEntity(x));
+ }
+
+ public void AssignRoles(string[] usernames, string[] roleNames)
+ {
+ using (var transaction = Database.GetTransaction())
+ {
+ //first get the member ids based on the usernames
+ var memberSql = new Sql();
+ var memberObjectType = new Guid(Constants.ObjectTypes.Member);
+ memberSql.Select("umbracoNode.id")
+ .From()
+ .InnerJoin()
+ .On(dto => dto.NodeId, dto => dto.NodeId)
+ .Where(x => x.NodeObjectType == memberObjectType)
+ .Where("cmsMember.LoginName in (@usernames)", new { usernames = usernames });
+ var memberIds = Database.Fetch(memberSql).ToArray();
+
+ AssignRolesInternal(memberIds, roleNames);
+ transaction.Complete();
+ }
+ }
+
+ public void DissociateRoles(string[] usernames, string[] roleNames)
+ {
+ using (var transaction = Database.GetTransaction())
+ {
+ //first get the member ids based on the usernames
+ var memberSql = new Sql();
+ var memberObjectType = new Guid(Constants.ObjectTypes.Member);
+ memberSql.Select("umbracoNode.id")
+ .From()
+ .InnerJoin()
+ .On(dto => dto.NodeId, dto => dto.NodeId)
+ .Where(x => x.NodeObjectType == memberObjectType)
+ .Where("cmsMember.LoginName in (@usernames)", new { usernames = usernames });
+ var memberIds = Database.Fetch(memberSql).ToArray();
+
+ DissociateRolesInternal(memberIds, roleNames);
+ transaction.Complete();
+ }
+ }
+
+ public void AssignRoles(int[] memberIds, string[] roleNames)
+ {
+ using (var transaction = Database.GetTransaction())
+ {
+ AssignRolesInternal(memberIds, roleNames);
+ transaction.Complete();
+ }
+ }
+
+ public void AssignRolesInternal(int[] memberIds, string[] roleNames)
+ {
+ //create the missing roles first
+
+ var existingSql = new Sql()
+ .Select("*")
+ .From()
+ .Where(dto => dto.NodeObjectType == NodeObjectTypeId)
+ .Where("umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + " in (@names)", new { names = roleNames });
+ var existingRoles = Database.Fetch(existingSql).Select(x => x.Text);
+ var missingRoles = roleNames.Except(existingRoles);
+ var missingGroups = missingRoles.Select(x => new MemberGroup {Name = x}).ToArray();
+
+ if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(missingGroups), this))
+ {
+ return;
+ }
+ foreach (var m in missingGroups)
+ {
+ PersistNewItem(m);
+ }
+ SavedMemberGroup.RaiseEvent(new SaveEventArgs