diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs
index 52b3533bc7..9140ee03f2 100644
--- a/src/Umbraco.Core/Constants-Conventions.cs
+++ b/src/Umbraco.Core/Constants-Conventions.cs
@@ -171,6 +171,11 @@ namespace Umbraco.Core
public const string FailedPasswordAttemptsLabel = "Failed Password Attempts";
+ ///
+ /// Group name to put the membership properties on
+ ///
+ internal const string StandardPropertiesGroupName = "Membership";
+
internal static Dictionary GetStandardPropertyTypeStubs()
{
return new Dictionary
diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs
index 6a54bddc70..d9f41b84d9 100644
--- a/src/Umbraco.Core/Models/Member.cs
+++ b/src/Umbraco.Core/Models/Member.cs
@@ -145,12 +145,22 @@ namespace Umbraco.Core.Models
{
get
{
+ if (Properties.Contains(Constants.Conventions.Member.PasswordQuestion) == false)
+ {
+ return default(string);
+ }
+
return Properties[Constants.Conventions.Member.PasswordQuestion].Value == null
? string.Empty
: Properties[Constants.Conventions.Member.PasswordQuestion].Value.ToString();
}
set
{
+ if (Properties.Contains(Constants.Conventions.Member.PasswordQuestion) == false)
+ {
+ return;
+ }
+
Properties[Constants.Conventions.Member.PasswordQuestion].Value = value;
}
}
@@ -167,12 +177,22 @@ namespace Umbraco.Core.Models
{
get
{
+ if (Properties.Contains(Constants.Conventions.Member.PasswordAnswer) == false)
+ {
+ return default(string);
+ }
+
return Properties[Constants.Conventions.Member.PasswordAnswer].Value == null
? string.Empty
: Properties[Constants.Conventions.Member.PasswordAnswer].Value.ToString();
}
set
{
+ if (Properties.Contains(Constants.Conventions.Member.PasswordAnswer) == false)
+ {
+ return;
+ }
+
Properties[Constants.Conventions.Member.PasswordAnswer].Value = value;
}
}
@@ -189,12 +209,22 @@ namespace Umbraco.Core.Models
{
get
{
+ if (Properties.Contains(Constants.Conventions.Member.Comments) == false)
+ {
+ return default(string);
+ }
+
return Properties[Constants.Conventions.Member.Comments].Value == null
? string.Empty
: Properties[Constants.Conventions.Member.Comments].Value.ToString();
}
set
{
+ if (Properties.Contains(Constants.Conventions.Member.Comments) == false)
+ {
+ return;
+ }
+
Properties[Constants.Conventions.Member.Comments].Value = value;
}
}
@@ -211,6 +241,12 @@ namespace Umbraco.Core.Models
{
get
{
+ if (Properties.Contains(Constants.Conventions.Member.IsApproved) == false)
+ {
+ //I guess we'll leave them approved by default!
+ return true;
+ }
+
var tryConvert = Properties[Constants.Conventions.Member.IsApproved].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -221,6 +257,11 @@ namespace Umbraco.Core.Models
}
set
{
+ if (Properties.Contains(Constants.Conventions.Member.IsApproved) == false)
+ {
+ return;
+ }
+
Properties[Constants.Conventions.Member.IsApproved].Value = value;
}
}
@@ -237,6 +278,12 @@ namespace Umbraco.Core.Models
{
get
{
+ if (Properties.Contains(Constants.Conventions.Member.IsLockedOut) == false)
+ {
+ //I guess we'll not lock them out by default!
+ return false;
+ }
+
var tryConvert = Properties[Constants.Conventions.Member.IsLockedOut].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -247,6 +294,11 @@ namespace Umbraco.Core.Models
}
set
{
+ if (Properties.Contains(Constants.Conventions.Member.IsLockedOut) == false)
+ {
+ return;
+ }
+
Properties[Constants.Conventions.Member.IsLockedOut].Value = value;
}
}
@@ -263,6 +315,11 @@ namespace Umbraco.Core.Models
{
get
{
+ if (Properties.Contains(Constants.Conventions.Member.LastLoginDate) == false)
+ {
+ return default(DateTime);
+ }
+
var tryConvert = Properties[Constants.Conventions.Member.LastLoginDate].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -273,6 +330,11 @@ namespace Umbraco.Core.Models
}
set
{
+ if (Properties.Contains(Constants.Conventions.Member.LastLoginDate) == false)
+ {
+ return;
+ }
+
Properties[Constants.Conventions.Member.LastLoginDate].Value = value;
}
}
@@ -289,6 +351,11 @@ namespace Umbraco.Core.Models
{
get
{
+ if (Properties.Contains(Constants.Conventions.Member.LastPasswordChangeDate) == false)
+ {
+ return default(DateTime);
+ }
+
var tryConvert = Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -299,6 +366,11 @@ namespace Umbraco.Core.Models
}
set
{
+ if (Properties.Contains(Constants.Conventions.Member.LastPasswordChangeDate) == false)
+ {
+ return;
+ }
+
Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value = value;
}
}
@@ -315,6 +387,11 @@ namespace Umbraco.Core.Models
{
get
{
+ if (Properties.Contains(Constants.Conventions.Member.LastLockoutDate) == false)
+ {
+ return default(DateTime);
+ }
+
var tryConvert = Properties[Constants.Conventions.Member.LastLockoutDate].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -325,6 +402,11 @@ namespace Umbraco.Core.Models
}
set
{
+ if (Properties.Contains(Constants.Conventions.Member.LastLockoutDate) == false)
+ {
+ return;
+ }
+
Properties[Constants.Conventions.Member.LastLockoutDate].Value = value;
}
}
@@ -342,6 +424,11 @@ namespace Umbraco.Core.Models
{
get
{
+ if (Properties.Contains(Constants.Conventions.Member.FailedPasswordAttempts) == false)
+ {
+ return 0;
+ }
+
var tryConvert = Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -352,7 +439,12 @@ namespace Umbraco.Core.Models
}
set
{
- Properties[Constants.Conventions.Member.LastLockoutDate].Value = value;
+ if (Properties.Contains(Constants.Conventions.Member.FailedPasswordAttempts) == false)
+ {
+ return;
+ }
+
+ Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value = value;
}
}
diff --git a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
index aff816515d..0c8ab00eda 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
{
@@ -25,7 +26,7 @@ namespace Umbraco.Core.Persistence.Querying
{
//if (TypeSerializer.CanCreateFromString(fieldType))
//{
- // return "'" + EscapeParam(TypeSerializer.SerializeToString(value)) + "'";
+ // return "'" + escapeCallback(TypeSerializer.SerializeToString(value)) + "'";
//}
throw new NotSupportedException(
@@ -46,27 +47,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)
- {
- return PetaPocoExtensions.EscapeAtSymbols(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 4dba620ab0..6ab3fcc592 100644
--- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs
+++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs
@@ -205,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)
@@ -233,15 +232,15 @@ namespace Umbraco.Core.Persistence.Querying
switch (verb)
{
case "SqlWildcard":
- return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, RemoveQuote(val), columnType);
case "Equals":
- return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, RemoveQuote(val), columnType);
case "StartsWith":
- return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnStartsWithComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnStartsWithComparison(col, RemoveQuote(val), columnType);
case "EndsWith":
- return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEndsWithComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEndsWithComparison(col, RemoveQuote(val), columnType);
case "Contains":
- return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnContainsComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
+ return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnContainsComparison(col, RemoveQuote(val), columnType);
case "InvariantEquals":
case "SqlEquals":
//recurse
diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs
index 6c2f7e6727..ecd5bb0087 100644
--- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs
+++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs
@@ -254,11 +254,11 @@ namespace Umbraco.Core.Persistence.Querying
case "ToLower":
return string.Format("lower({0})", r);
case "StartsWith":
- return string.Format("upper({0}) like '{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString().ToUpper())));
+ return string.Format("upper({0}) like '{1}%'", r, RemoveQuote(args[0].ToString().ToUpper()));
case "EndsWith":
- return string.Format("upper({0}) like '%{1}'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper()));
+ return string.Format("upper({0}) like '%{1}'", r, RemoveQuote(args[0].ToString()).ToUpper());
case "Contains":
- return string.Format("upper({0}) like '%{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper()));
+ return string.Format("upper({0}) like '%{1}%'", r, RemoveQuote(args[0].ToString()).ToUpper());
case "Substring":
var startIndex = Int32.Parse(args[0].ToString()) + 1;
if (args.Count == 2)
diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs
index 08bed74923..aecbe77a96 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs
@@ -168,10 +168,11 @@ namespace Umbraco.Core.Persistence.Repositories
((MemberType)entity).AddingEntity();
//By Convention we add 9 stnd PropertyTypes to an Umbraco MemberType
+ entity.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupName);
var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
foreach (var standardPropertyType in standardPropertyTypes)
{
- entity.AddPropertyType(standardPropertyType.Value);
+ entity.AddPropertyType(standardPropertyType.Value, Constants.Conventions.Member.StandardPropertiesGroupName);
}
var factory = new MemberTypeFactory(NodeObjectTypeId);
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs
index 2b31fb2032..c62fa1a923 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs
@@ -11,6 +11,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
///
public interface ISqlSyntaxProvider
{
+ string EscapeString(string val);
+
string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType);
string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType);
string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType);
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs
index 5f5d412ab3..0b8d80a21f 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs
@@ -327,5 +327,10 @@ namespace Umbraco.Core.Persistence.SqlSyntax
// add message to check with their hosting provider
return supportsCaseInsensitiveQueries;
}
+
+ public override string EscapeString(string val)
+ {
+ return PetaPocoExtensions.EscapeAtSymbols(MySql.Data.MySqlClient.MySqlHelper.EscapeString(val));
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs
index 8f9a84437c..004aabfc70 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs
@@ -103,6 +103,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
DbTypeMap.Set(DbType.Binary, BlobColumnDefinition);
}
+ public virtual string EscapeString(string val)
+ {
+ return PetaPocoExtensions.EscapeAtSymbols(val.Replace("'", "''"));
+ }
+
public virtual string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index bca0b0adce..1367cdcca3 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -58,10 +58,15 @@
..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll
-
+
+ False
+ ..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll
+
+
False
..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
+
diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config
index cc673f88a1..c2c45f4195 100644
--- a/src/Umbraco.Core/packages.config
+++ b/src/Umbraco.Core/packages.config
@@ -11,5 +11,6 @@
+
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Persistence/Querying/ExpressionTests.cs b/src/Umbraco.Tests/Persistence/Querying/ExpressionTests.cs
index c0316e979e..ba1592635f 100644
--- a/src/Umbraco.Tests/Persistence/Querying/ExpressionTests.cs
+++ b/src/Umbraco.Tests/Persistence/Querying/ExpressionTests.cs
@@ -2,7 +2,10 @@
using System.Linq.Expressions;
using NUnit.Framework;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Querying;
+using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Persistence.Querying
@@ -35,5 +38,60 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.AreEqual("[umbracoNode].[parentID] = -1", result);
}
+
+ [Test]
+ public void Equals_Operator_For_Value_Gets_Escaped()
+ {
+ Expression> predicate = user => user.Username == "hello@world.com";
+ var modelToSqlExpressionHelper = new ModelToSqlExpressionHelper();
+ var result = modelToSqlExpressionHelper.Visit(predicate);
+
+ Console.WriteLine("Model to Sql ExpressionHelper: \n" + result);
+
+ Assert.AreEqual("[umbracoUser].[userLogin] = 'hello@@world.com'", result);
+ }
+
+ [Test]
+ public void Equals_Method_For_Value_Gets_Escaped()
+ {
+ Expression> predicate = user => user.Username.Equals("hello@world.com");
+ var modelToSqlExpressionHelper = new ModelToSqlExpressionHelper();
+ var result = modelToSqlExpressionHelper.Visit(predicate);
+
+ Console.WriteLine("Model to Sql ExpressionHelper: \n" + result);
+
+ Assert.AreEqual("upper([umbracoUser].[userLogin]) = 'HELLO@@WORLD.COM'", result);
+ }
+
+ [Test]
+ public void Model_Expression_Value_Does_Not_Get_Double_Escaped()
+ {
+ //mysql escapes backslashes, so we'll test with that
+ SqlSyntaxContext.SqlSyntaxProvider = MySqlSyntax.Provider;
+
+ Expression> predicate = user => user.Username.Equals("mydomain\\myuser");
+ var modelToSqlExpressionHelper = new ModelToSqlExpressionHelper();
+ var result = modelToSqlExpressionHelper.Visit(predicate);
+
+ Console.WriteLine("Model to Sql ExpressionHelper: \n" + result);
+
+ Assert.AreEqual("upper(`umbracoUser`.`userLogin`) = 'MYDOMAIN\\\\MYUSER'", result);
+ }
+
+ [Test]
+ public void Poco_Expression_Value_Does_Not_Get_Double_Escaped()
+ {
+ //mysql escapes backslashes, so we'll test with that
+ SqlSyntaxContext.SqlSyntaxProvider = MySqlSyntax.Provider;
+
+ Expression> predicate = user => user.Login.StartsWith("mydomain\\myuser");
+ var modelToSqlExpressionHelper = new PocoToSqlExpressionHelper();
+ var result = modelToSqlExpressionHelper.Visit(predicate);
+
+ Console.WriteLine("Poco to Sql ExpressionHelper: \n" + result);
+
+ Assert.AreEqual("upper(`umbracoUser`.`userLogin`) like 'MYDOMAIN\\\\MYUSER%'", result);
+ }
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs
index 3bd9c2001f..bb9cd553a9 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs
@@ -224,10 +224,17 @@ namespace Umbraco.Tests.Persistence.Repositories
var sut = repository.Get(member.Id);
- Assert.That(sut.ContentType.PropertyGroups.Count(), Is.EqualTo(1));
+ Assert.That(sut.ContentType.PropertyGroups.Count(), Is.EqualTo(2));
Assert.That(sut.ContentType.PropertyTypes.Count(), Is.EqualTo(3 + Constants.Conventions.Member.GetStandardPropertyTypeStubs().Count));
Assert.That(sut.Properties.Count(), Is.EqualTo(3 + Constants.Conventions.Member.GetStandardPropertyTypeStubs().Count));
Assert.That(sut.Properties.Any(x => x.HasIdentity == false || x.Id == 0), Is.False);
+ var grp = sut.PropertyGroups.FirstOrDefault(x => x.Name == Constants.Conventions.Member.StandardPropertiesGroupName);
+ Assert.IsNotNull(grp);
+ var aliases = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray();
+ foreach (var p in sut.PropertyTypes.Where(x => aliases.Contains(x.Alias)))
+ {
+ Assert.AreEqual(grp.Id, p.PropertyGroupId.Value);
+ }
}
}
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index f247296790..96238cff03 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -160,7 +160,7 @@
False
..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll
-
+
False
..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll
diff --git a/src/Umbraco.Web.UI/config/ClientDependency.config b/src/Umbraco.Web.UI/config/ClientDependency.config
index 1c576e7e4e..10b06e9f93 100644
--- a/src/Umbraco.Web.UI/config/ClientDependency.config
+++ b/src/Umbraco.Web.UI/config/ClientDependency.config
@@ -10,7 +10,7 @@ NOTES:
* Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config
* A new version will invalidate both client and server cache and create new persisted files
-->
-
+