Merge branch '6.2.0' of https://github.com/umbraco/Umbraco-CMS into 6.2.0

This commit is contained in:
Morten Christensen
2013-12-18 10:00:47 +01:00
46 changed files with 2171 additions and 374 deletions

View File

@@ -166,140 +166,84 @@ namespace Umbraco.Core
public const string FailedPasswordAttemptsLabel = "Failed Password Attempts";
internal static Dictionary<string, PropertyType>
StandardPropertyTypeStubs = new Dictionary<string, PropertyType>
{
{
Comments,
new PropertyType(
new Guid(
PropertyEditors
.TextboxMultiple),
DataTypeDatabaseType
.Ntext)
{
Alias = Comments,
Name =
CommentsLabel
}
},
{
FailedPasswordAttempts,
new PropertyType(
new Guid(
PropertyEditors
.Integer),
DataTypeDatabaseType
.Integer)
{
Alias =
FailedPasswordAttempts,
Name =
FailedPasswordAttemptsLabel
}
},
{
IsApproved,
new PropertyType(
new Guid(
PropertyEditors
.TrueFalse),
DataTypeDatabaseType
.Integer)
{
Alias = IsApproved,
Name =
IsApprovedLabel
}
},
{
IsLockedOut,
new PropertyType(
new Guid(
PropertyEditors
.TrueFalse),
DataTypeDatabaseType
.Integer)
{
Alias =
IsLockedOut,
Name =
IsLockedOutLabel
}
},
{
LastLockoutDate,
new PropertyType(
new Guid(
PropertyEditors.Date),
DataTypeDatabaseType
.Date)
{
Alias =
LastLockoutDate,
Name =
LastLockoutDateLabel
}
},
{
LastLoginDate,
new PropertyType(
new Guid(
PropertyEditors.Date),
DataTypeDatabaseType
.Date)
{
Alias =
LastLoginDate,
Name =
LastLoginDateLabel
}
},
{
LastPasswordChangeDate,
new PropertyType(
new Guid(
PropertyEditors.Date),
DataTypeDatabaseType
.Date)
{
Alias =
LastPasswordChangeDate,
Name =
LastPasswordChangeDateLabel
}
},
{
PasswordAnswer,
new PropertyType(
new Guid(
PropertyEditors
.Textbox),
DataTypeDatabaseType
.Nvarchar)
{
Alias =
PasswordAnswer,
Name =
PasswordAnswerLabel
}
},
{
PasswordQuestion,
new PropertyType(
new Guid(
PropertyEditors
.Textbox),
DataTypeDatabaseType
.Nvarchar)
{
Alias =
PasswordQuestion,
Name =
PasswordQuestionLabel
}
}
};
internal static Dictionary<string, PropertyType> GetStandardPropertyTypeStubs()
{
return new Dictionary<string, PropertyType>
{
{
Comments,
new PropertyType(new Guid(PropertyEditors.TextboxMultiple), DataTypeDatabaseType.Ntext)
{
Alias = Comments,
Name = CommentsLabel
}
},
{
FailedPasswordAttempts,
new PropertyType(new Guid(PropertyEditors.Integer), DataTypeDatabaseType.Integer)
{
Alias = FailedPasswordAttempts,
Name = FailedPasswordAttemptsLabel
}
},
{
IsApproved,
new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer)
{
Alias = IsApproved,
Name = IsApprovedLabel
}
},
{
IsLockedOut,
new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer)
{
Alias = IsLockedOut,
Name = IsLockedOutLabel
}
},
{
LastLockoutDate,
new PropertyType(new Guid(PropertyEditors.Date), DataTypeDatabaseType.Date)
{
Alias = LastLockoutDate,
Name = LastLockoutDateLabel
}
},
{
LastLoginDate,
new PropertyType(new Guid(PropertyEditors.Date), DataTypeDatabaseType.Date)
{
Alias = LastLoginDate,
Name = LastLoginDateLabel
}
},
{
LastPasswordChangeDate,
new PropertyType(new Guid(PropertyEditors.Date), DataTypeDatabaseType.Date)
{
Alias = LastPasswordChangeDate,
Name = LastPasswordChangeDateLabel
}
},
{
PasswordAnswer,
new PropertyType(new Guid(PropertyEditors.Textbox), DataTypeDatabaseType.Nvarchar)
{
Alias = PasswordAnswer,
Name = PasswordAnswerLabel
}
},
{
PasswordQuestion,
new PropertyType(new Guid(PropertyEditors.Textbox), DataTypeDatabaseType.Nvarchar)
{
Alias = PasswordQuestion,
Name = PasswordQuestionLabel
}
}
};
}
}
/// <summary>

View File

@@ -613,11 +613,16 @@ namespace Umbraco.Core.Models
return ApplicationContext.Current.Services.PackagingService.Export(media);
}
/// <summary>
/// Creates the full xml representation for the <see cref="IMedia"/> object and all of it's descendants
/// </summary>
/// <param name="media"><see cref="IMedia"/> to generate xml for</param>
/// <returns>Xml representation of the passed in <see cref="IMedia"/></returns>
internal static XElement ToDeepXml(this IMedia media)
{
return ApplicationContext.Current.Services.PackagingService.Export(media, true);
}
/// <summary>
/// Creates the xml representation for the <see cref="IContent"/> object
/// </summary>
@@ -630,5 +635,15 @@ namespace Umbraco.Core.Models
//If current IContent is published we should get latest unpublished version
return content.ToXml();
}
/// <summary>
/// Creates the xml representation for the <see cref="IMember"/> object
/// </summary>
/// <param name="member"><see cref="IMember"/> to generate xml for</param>
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
public static XElement ToXml(this IMember member)
{
return ApplicationContext.Current.Services.PackagingService.Export(member);
}
}
}

View File

@@ -43,6 +43,7 @@ namespace Umbraco.Core.Models
_allowedContentTypes = new List<ContentTypeSort>();
_propertyGroups = new PropertyGroupCollection();
_propertyTypes = new PropertyTypeCollection();
_propertyTypes.CollectionChanged += PropertyTypesChanged;
_additionalData = new Dictionary<string, object>();
}
@@ -54,6 +55,7 @@ namespace Umbraco.Core.Models
_allowedContentTypes = new List<ContentTypeSort>();
_propertyGroups = new PropertyGroupCollection();
_propertyTypes = new PropertyTypeCollection();
_propertyTypes.CollectionChanged += PropertyTypesChanged;
_additionalData = new Dictionary<string, object>();
}
@@ -428,8 +430,7 @@ namespace Umbraco.Core.Models
{
if (PropertyTypeExists(propertyType.Alias) == false)
{
_propertyTypes.Add(propertyType);
_propertyTypes.CollectionChanged += PropertyTypesChanged;
_propertyTypes.Add(propertyType);
return true;
}

View File

@@ -20,7 +20,18 @@ namespace Umbraco.Core.Models
private object _providerUserKey;
private Type _userTypeKey;
public Member(string name, string email, string username, string password, int parentId, IMemberType contentType)
/// <summary>
/// Constructor for creating a Member object
/// </summary>
/// <param name="name">Name of the content</param>
/// <param name="contentType">ContentType for the current Content object</param>
public Member(string name, IMemberType contentType)
: base(name, -1, contentType, new PropertyCollection())
{
_contentType = contentType;
}
internal Member(string name, string email, string username, string password, int parentId, IMemberType contentType)
: base(name, parentId, contentType, new PropertyCollection())
{
Mandate.ParameterNotNull(contentType, "contentType");
@@ -31,8 +42,8 @@ namespace Umbraco.Core.Models
_password = password;
}
public Member(string name, string email, string username, string password, IContentBase parent, IMemberType contentType)
: base(name, parent, contentType, new PropertyCollection())
public Member(string name, string email, string username, string password, IMemberType contentType)
: this(name, email, username, password, -1, contentType)
{
Mandate.ParameterNotNull(contentType, "contentType");
@@ -42,6 +53,17 @@ namespace Umbraco.Core.Models
_password = password;
}
//public Member(string name, string email, string username, string password, IContentBase parent, IMemberType contentType)
// : base(name, parent, contentType, new PropertyCollection())
//{
// Mandate.ParameterNotNull(contentType, "contentType");
// _contentType = contentType;
// _email = email;
// _username = username;
// _password = password;
//}
private static readonly PropertyInfo DefaultContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.ContentTypeAlias);
private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.Username);
private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.Email);

View File

@@ -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")]

View File

@@ -36,7 +36,7 @@ namespace Umbraco.Core.Persistence.Factories
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 standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
foreach (var standardPropertyType in standardPropertyTypes)
{
if(dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue;

View File

@@ -21,14 +21,18 @@ namespace Umbraco.Core.Persistence
/// <summary>
/// This will escape single @ symbols for peta poco values so it doesn't think it's a parameter
/// </summary>
/// <param name="db"></param>
/// <param name="value"></param>
/// <returns></returns>
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("(?<!@)@(?!@)");
return regex.Replace(value, "@@");
if (value.Contains("@"))
{
//this fancy regex will only match a single @ not a double, etc...
var regex = new Regex("(?<!@)@(?!@)");
return regex.Replace(value, "@@");
}
return value;
}
public static void CreateTable<T>(this Database db)

View File

@@ -64,13 +64,7 @@ namespace Umbraco.Core.Persistence.Querying
public virtual string EscapeAtArgument(string exp)
{
/*if (exp.StartsWith("@"))
return string.Concat("@", exp);*/
if (exp.Contains("@"))
return exp.Replace("@", "@@");
return exp;
return PetaPocoExtensions.EscapeAtSymbols(exp);
}
public virtual bool ShouldQuoteValue(Type fieldType)

View File

@@ -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
{
@@ -226,6 +228,39 @@ namespace Umbraco.Core.Persistence.Querying
}
private string HandleStringComparison(string col, string val, string verb, TextColumnType columnType)
{
switch (verb)
{
case "Equals":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
case "StartsWith":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnStartsWithComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
case "EndsWith":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEndsWithComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
case "Contains":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnContainsComparison(col, EscapeAtArgument(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");
}
}
protected virtual string VisitMethodCall(MethodCallExpression m)
{
List<Object> args = this.VisitExpressionList(m.Arguments);
@@ -245,12 +280,31 @@ namespace Umbraco.Core.Persistence.Querying
return string.Format("upper({0})", r);
case "ToLower":
return string.Format("lower({0})", r);
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)

View File

@@ -0,0 +1,30 @@
using System;
namespace Umbraco.Core.Persistence.Querying
{
/// <summary>
/// String extension methods used specifically to translate into SQL
/// </summary>
internal static class SqlStringExtensions
{
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);
}
}
}

View File

@@ -0,0 +1,13 @@
namespace Umbraco.Core.Persistence.Querying
{
/// <summary>
/// Determines how to match a string property value
/// </summary>
public enum StringPropertyMatchType
{
Exact,
Contains,
StartsWith,
EndsWith
}
}

View File

@@ -0,0 +1,8 @@
namespace Umbraco.Core.Persistence.Querying
{
public enum TextColumnType
{
NVarchar,
NText
}
}

View File

@@ -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;

View File

@@ -22,14 +22,17 @@ namespace Umbraco.Core.Persistence.Repositories
{
private readonly IMemberTypeRepository _memberTypeRepository;
public MemberRepository(IDatabaseUnitOfWork work, IMemberTypeRepository memberTypeRepository) : base(work)
public MemberRepository(IDatabaseUnitOfWork work, IMemberTypeRepository memberTypeRepository)
: base(work)
{
if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository");
_memberTypeRepository = memberTypeRepository;
}
public MemberRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IMemberTypeRepository memberTypeRepository)
: base(work, cache)
{
if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository");
_memberTypeRepository = memberTypeRepository;
}
@@ -104,11 +107,11 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Select("umbracoNode.*", "cmsContent.contentType", "cmsContentType.alias AS ContentTypeAlias", "cmsContentVersion.VersionId",
"cmsContentVersion.VersionDate", "cmsContentVersion.LanguageLocale", "cmsMember.Email",
"cmsMember.LoginName", "cmsMember.Password", "cmsPropertyData.id AS PropertyDataId", "cmsPropertyData.propertytypeid",
"cmsMember.LoginName", "cmsMember.Password", "cmsPropertyData.id AS PropertyDataId", "cmsPropertyData.propertytypeid",
"cmsPropertyData.dataDate", "cmsPropertyData.dataInt", "cmsPropertyData.dataNtext", "cmsPropertyData.dataNvarchar",
"cmsPropertyType.id", "cmsPropertyType.Alias", "cmsPropertyType.Description",
"cmsPropertyType.Name", "cmsPropertyType.mandatory", "cmsPropertyType.validationRegExp",
"cmsPropertyType.helpText", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder", "cmsPropertyType.propertyTypeGroupId",
"cmsPropertyType.helpText", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder", "cmsPropertyType.propertyTypeGroupId",
"cmsPropertyType.dataTypeId", "cmsDataType.controlId", "cmsDataType.dbType")
.From<NodeDto>()
.InnerJoin<ContentDto>().On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
@@ -247,7 +250,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
property.Id = keyDictionary[property.PropertyTypeId];
}
((Member)entity).ResetDirtyProperties();
}
@@ -256,8 +259,10 @@ namespace Umbraco.Core.Persistence.Repositories
//Updates Modified date
((Member)entity).UpdatingEntity();
var dirtyEntity = (ICanBeDirty)entity;
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
if (((ICanBeDirty)entity).IsPropertyDirty("ParentId"))
if (dirtyEntity.IsPropertyDirty("ParentId"))
{
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { ParentId = ((IUmbracoEntity)entity).ParentId });
((IUmbracoEntity)entity).Path = string.Concat(parent.Path, ",", entity.Id);
@@ -293,13 +298,32 @@ namespace Umbraco.Core.Persistence.Repositories
//Updates the current version - cmsContentVersion
//Assumes a Version guid exists and Version date (modified date) has been set/updated
Database.Update(dto.ContentVersionDto);
//Updates the cmsMember entry
Database.Update(dto);
//Updates the cmsMember entry if it has changed
var changedCols = new List<string>();
if (dirtyEntity.IsPropertyDirty("Email"))
{
changedCols.Add("Email");
}
if (dirtyEntity.IsPropertyDirty("Username"))
{
changedCols.Add("LoginName");
}
// DO NOT update the password if it is null or empty
if (dirtyEntity.IsPropertyDirty("Password") && entity.Password.IsNullOrWhiteSpace() == false)
{
changedCols.Add("Password");
}
//only update the changed cols
if (changedCols.Count > 0)
{
Database.Update(dto, changedCols);
}
//TODO ContentType for the Member entity
//Create the PropertyData for this version - cmsPropertyData
var propertyFactory = new PropertyFactory(entity.ContentType, entity.Version, entity.Id);
var propertyFactory = new PropertyFactory(entity.ContentType, entity.Version, entity.Id);
var keyDictionary = new Dictionary<int, int>();
//Add Properties
@@ -333,8 +357,8 @@ namespace Umbraco.Core.Persistence.Repositories
property.Id = keyDictionary[property.PropertyTypeId];
}
}
((ICanBeDirty)entity).ResetDirtyProperties();
dirtyEntity.ResetDirtyProperties();
}
protected override void PersistDeletedItem(IMember entity)
@@ -416,9 +440,10 @@ namespace Umbraco.Core.Persistence.Repositories
public bool Exists(string username)
{
var sql = new Sql();
var escapedUserName = PetaPocoExtensions.EscapeAtSymbols(username);
sql.Select("COUNT(*)")
.From<MemberDto>()
.Where<MemberDto>(x => x.LoginName == username);
.Where<MemberDto>(x => x.LoginName == escapedUserName);
return Database.ExecuteScalar<int>(sql) > 0;
}

View File

@@ -168,7 +168,7 @@ namespace Umbraco.Core.Persistence.Repositories
((MemberType)entity).AddingEntity();
//By Convention we add 9 stnd PropertyTypes to an Umbraco MemberType
var standardPropertyTypes = Constants.Conventions.Member.StandardPropertyTypeStubs;
var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
foreach (var standardPropertyType in standardPropertyTypes)
{
entity.AddPropertyType(standardPropertyType.Value);

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
@@ -10,6 +11,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// </summary>
public interface ISqlSyntaxProvider
{
string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType);
string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType);
string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType);
string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType);
string GetQuotedTableName(string tableName);
string GetQuotedColumnName(string columnName);
string GetQuotedName(string name);

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the MySql SqlSyntax Provider
/// </summary>
internal static class MySqlSyntax
{
public static ISqlSyntaxProvider Provider { get { return new MySqlSyntaxProvider(); } }
}
}

View File

@@ -6,14 +6,6 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the MySql SqlSyntax Provider
/// </summary>
internal static class MySqlSyntax
{
public static ISqlSyntaxProvider Provider { get { return new MySqlSyntaxProvider(); } }
}
/// <summary>
/// Represents an SqlSyntaxProvider for MySql
/// </summary>

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the Sql CE SqlSyntax Provider
/// </summary>
internal static class SqlCeSyntax
{
public static ISqlSyntaxProvider Provider { get { return new SqlCeSyntaxProvider(); } }
}
}

View File

@@ -3,17 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the Sql CE SqlSyntax Provider
/// </summary>
internal static class SqlCeSyntax
{
public static ISqlSyntaxProvider Provider { get { return new SqlCeSyntaxProvider(); } }
}
/// <summary>
/// Represents an SqlSyntaxProvider for Sql Ce
/// </summary>
@@ -67,6 +60,62 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return indexType;
}
public override string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnEqualComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for = comparison with NText columns but allows this syntax
return string.Format("{0} LIKE '{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnStartsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnEndsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnContainsComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetQuotedTableName(string tableName)
{
return string.Format("[{0}]", tableName);

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the Sql Server SqlSyntax Provider
/// </summary>
internal static class SqlServerSyntax
{
public static ISqlSyntaxProvider Provider { get { return new SqlServerSyntaxProvider(); } }
}
}

View File

@@ -2,31 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the Sql Server SqlSyntax Provider
/// </summary>
internal static class SqlServerSyntax
{
public static ISqlSyntaxProvider Provider { get { return new SqlServerSyntaxProvider(); } }
}
/// <summary>
/// Represents the version name of SQL server (i.e. the year 2008, 2005, etc...)
/// </summary>
internal enum SqlServerVersionName
{
Invalid = -1,
V7 = 0,
V2000 = 1,
V2005 = 2,
V2008 = 3,
V2012 = 4,
Other = 5
}
/// <summary>
/// Represents an SqlSyntaxProvider for Sql Server
/// </summary>
@@ -55,6 +34,62 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// </summary>
internal Lazy<SqlServerVersionName> VersionName { get; set; }
public override string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnEqualComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for = comparison with NText columns but allows this syntax
return string.Format("{0} LIKE '{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnStartsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnEndsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnContainsComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetQuotedTableName(string tableName)
{
return string.Format("[{0}]", tableName);

View File

@@ -0,0 +1,16 @@
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Represents the version name of SQL server (i.e. the year 2008, 2005, etc...)
/// </summary>
internal enum SqlServerVersionName
{
Invalid = -1,
V7 = 0,
V2000 = 1,
V2005 = 2,
V2008 = 3,
V2012 = 4,
Other = 5
}
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
@@ -102,6 +103,30 @@ namespace Umbraco.Core.Persistence.SqlSyntax
DbTypeMap.Set<byte[]>(DbType.Binary, BlobColumnDefinition);
}
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.
return string.Format("upper({0}) = '{1}'", column, value.ToUpper());
}
public virtual string GetStringColumnStartsWithComparison(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.
return string.Format("upper({0}) like '{1}%'", column, value.ToUpper());
}
public virtual string GetStringColumnEndsWithComparison(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.
return string.Format("upper({0}) like '%{1}'", column, value.ToUpper());
}
public virtual string GetStringColumnContainsComparison(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.
return string.Format("upper({0}) like '%{1}%'", column, value.ToUpper());
}
public virtual string GetQuotedTableName(string tableName)
{
return string.Format("\"{0}\"", tableName);

View File

@@ -10,7 +10,6 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
@@ -21,7 +20,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Represents the ContentType Service, which is an easy access to operations involving <see cref="IContentType"/>
/// </summary>
public class ContentTypeService : IContentTypeService
public class ContentTypeService : ContentTypeServiceBase, IContentTypeService
{
private readonly RepositoryFactory _repositoryFactory;
private readonly IContentService _contentService;
@@ -147,48 +146,7 @@ namespace Umbraco.Core.Services
private void UpdateContentXmlStructure(params IContentTypeBase[] contentTypes)
{
var toUpdate = new List<IContentTypeBase>();
foreach (var contentType in contentTypes)
{
//we need to determine if we need to refresh the xml content in the database. This is to be done when:
// - the item is not new (already existed in the db) AND
// - a content type changes it's alias OR
// - if a content type has it's property removed OR
// - if a content type has a property whose alias has changed
//here we need to check if the alias of the content type changed or if one of the properties was removed.
var dirty = contentType as IRememberBeingDirty;
if (dirty == null) continue;
//check if any property types have changed their aliases (and not new property types)
var hasAnyPropertiesChangedAlias = contentType.PropertyTypes.Any(propType =>
{
var dirtyProperty = propType as IRememberBeingDirty;
if (dirtyProperty == null) return false;
return dirtyProperty.WasPropertyDirty("HasIdentity") == false //ensure it's not 'new'
&& dirtyProperty.WasPropertyDirty("Alias"); //alias has changed
});
if (dirty.WasPropertyDirty("HasIdentity") == false //ensure it's not 'new'
&& (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") || hasAnyPropertiesChangedAlias))
{
//If the alias was changed then we only need to update the xml structures for content of the current content type.
//If a property was deleted or a property alias was changed then we need to update the xml structures for any
// content of the current content type and any of the content type's child content types.
if (dirty.WasPropertyDirty("Alias")
&& dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") == false && hasAnyPropertiesChangedAlias == false)
{
//if only the alias changed then only update the current content type
toUpdate.Add(contentType);
}
else
{
//if a property was deleted or alias changed, then update all content of the current content type
// and all of it's desscendant doc types.
toUpdate.AddRange(contentType.DescendantsAndSelf());
}
}
}
var toUpdate = GetContentTypesForXmlUpdates(contentTypes).ToArray();
if (toUpdate.Any())
{

View File

@@ -0,0 +1,65 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Services
{
public class ContentTypeServiceBase
{
/// <summary>
/// This is called after an content type is saved and is used to update the content xml structures in the database
/// if they are required to be updated.
/// </summary>
/// <param name="contentTypes"></param>
internal IEnumerable<IContentTypeBase> GetContentTypesForXmlUpdates(params IContentTypeBase[] contentTypes)
{
var toUpdate = new List<IContentTypeBase>();
foreach (var contentType in contentTypes)
{
//we need to determine if we need to refresh the xml content in the database. This is to be done when:
// - the item is not new (already existed in the db) AND
// - a content type changes it's alias OR
// - if a content type has it's property removed OR
// - if a content type has a property whose alias has changed
//here we need to check if the alias of the content type changed or if one of the properties was removed.
var dirty = contentType as IRememberBeingDirty;
if (dirty == null) continue;
//check if any property types have changed their aliases (and not new property types)
var hasAnyPropertiesChangedAlias = contentType.PropertyTypes.Any(propType =>
{
var dirtyProperty = propType as IRememberBeingDirty;
if (dirtyProperty == null) return false;
return dirtyProperty.WasPropertyDirty("HasIdentity") == false //ensure it's not 'new'
&& dirtyProperty.WasPropertyDirty("Alias"); //alias has changed
});
if (dirty.WasPropertyDirty("HasIdentity") == false //ensure it's not 'new'
&& (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") || hasAnyPropertiesChangedAlias))
{
//If the alias was changed then we only need to update the xml structures for content of the current content type.
//If a property was deleted or a property alias was changed then we need to update the xml structures for any
// content of the current content type and any of the content type's child content types.
if (dirty.WasPropertyDirty("Alias")
&& dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") == false && hasAnyPropertiesChangedAlias == false)
{
//if only the alias changed then only update the current content type
toUpdate.Add(contentType);
}
else
{
//if a property was deleted or alias changed, then update all content of the current content type
// and all of it's desscendant doc types.
toUpdate.AddRange(contentType.DescendantsAndSelf());
}
}
}
return toUpdate;
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Services
{
@@ -19,37 +20,15 @@ namespace Umbraco.Core.Services
IMember GetById(int id);
IMember GetByKey(Guid id);
IEnumerable<IMember> GetMembersByMemberType(string memberTypeAlias);
IEnumerable<IMember> GetMembersByMemberType(int memberTypeId);
IEnumerable<IMember> GetMembersByGroup(string memberGroupName);
IEnumerable<IMember> GetAllMembers(params int[] ids);
}
void DeleteMembersOfType(int memberTypeId);
/// <summary>
/// Defines part of the MemberService, which is specific to methods used by the membership provider.
/// </summary>
/// <remarks>
/// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl.
/// </remarks>
internal interface IMembershipMemberService : IService
{
/// <summary>
/// Checks if a member with the username exists
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
bool Exists(string username);
IMember CreateMember(string username, string email, string password, string memberTypeAlias, int userId = 0);
IMember GetById(object id);
IMember GetByEmail(string email);
IMember GetByUsername(string login);
void Delete(IMember membershipUser);
void Save(IMember membershipUser);
IEnumerable<IMember> FindMembersByEmail(string emailStringToMatch);
IEnumerable<IMember> GetMembersByPropertyValue(string propertyTypeAlias, string value, StringPropertyMatchType matchType = StringPropertyMatchType.Exact);
IEnumerable<IMember> GetMembersByPropertyValue(string propertyTypeAlias, int value);
IEnumerable<IMember> GetMembersByPropertyValue(string propertyTypeAlias, bool value);
IEnumerable<IMember> GetMembersByPropertyValue(string propertyTypeAlias, DateTime value);
}
}

View File

@@ -25,5 +25,35 @@ namespace Umbraco.Core.Services
/// <param name="alias">Alias of the <see cref="IMediaType"/> to retrieve</param>
/// <returns><see cref="IMediaType"/></returns>
IMemberType GetMemberType(string alias);
/// <summary>
/// Saves a single <see cref="IMemberType"/> object
/// </summary>
/// <param name="memberType"><see cref="IMemberType"/> to save</param>
/// <param name="userId">Optional Id of the User saving the ContentType</param>
void Save(IMemberType memberType, int userId = 0);
/// <summary>
/// Saves a collection of <see cref="IMemberType"/> objects
/// </summary>
/// <param name="memberTypes">Collection of <see cref="IMemberType"/> to save</param>
/// <param name="userId">Optional Id of the User saving the ContentTypes</param>
void Save(IEnumerable<IMemberType> memberTypes, int userId = 0);
/// <summary>
/// Deletes a single <see cref="IMemberType"/> object
/// </summary>
/// <param name="memberType"><see cref="IMemberType"/> to delete</param>
/// <remarks>Deleting a <see cref="IMemberType"/> will delete all the <see cref="IContent"/> objects based on this <see cref="IMemberType"/></remarks>
/// <param name="userId">Optional Id of the User deleting the ContentType</param>
void Delete(IMemberType memberType, int userId = 0);
/// <summary>
/// Deletes a collection of <see cref="IMemberType"/> objects
/// </summary>
/// <param name="memberTypes">Collection of <see cref="IMemberType"/> to delete</param>
/// <remarks>Deleting a <see cref="IMemberType"/> will delete all the <see cref="IContent"/> objects based on this <see cref="IMemberType"/></remarks>
/// <param name="userId">Optional Id of the User deleting the ContentTypes</param>
void Delete(IEnumerable<IMemberType> memberTypes, int userId = 0);
}
}

View File

@@ -0,0 +1,48 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Services
{
/// <summary>
/// Defines part of the MemberService, which is specific to methods used by the membership provider.
/// </summary>
/// <remarks>
/// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl.
/// </remarks>
internal interface IMembershipMemberService : IService
{
/// <summary>
/// Checks if a member with the username exists
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
bool Exists(string username);
/// <summary>
/// Creates and persists a new member
/// </summary>
/// <param name="username"></param>
/// <param name="email"></param>
/// <param name="password"></param>
/// <param name="memberTypeAlias"></param>
/// <returns></returns>
IMember CreateMember(string username, string email, string password, string memberTypeAlias);
IMember GetById(object id);
IMember GetByEmail(string email);
IMember GetByUsername(string login);
void Delete(IMember membershipUser);
void Save(IMember membershipUser, bool raiseEvents = true);
void Save(IEnumerable<IMember> members, bool raiseEvents = true);
IEnumerable<IMember> FindMembersByEmail(string emailStringToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith);
IEnumerable<IMember> FindMembersByUsername(string login, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith);
}
}

View File

@@ -598,7 +598,7 @@ namespace Umbraco.Core.Services
//The ContentType has to be removed from the composition somehow as it would otherwise break
//Dbl.check+test that the ContentType's Id is removed from the ContentType2ContentType table
var query = Query<IMedia>.Builder.Where(x => x.ContentTypeId == mediaTypeId);
var contents = repository.GetByQuery(query);
var contents = repository.GetByQuery(query).ToArray();
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IMedia>(contents), this))
return;
@@ -760,30 +760,31 @@ namespace Umbraco.Core.Services
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMedia>(medias), this))
return;
}
using (new WriteLock(Locker))
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
{
foreach (var media in medias)
{
media.CreatorId = userId;
repository.AddOrUpdate(media);
}
var mediaXml = new Dictionary<int, Lazy<XElement>>();
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
{
foreach (var media in medias)
{
media.CreatorId = userId;
repository.AddOrUpdate(media);
}
//commit the whole lot in one go
uow.Commit();
//commit the whole lot in one go
uow.Commit();
foreach (var media in medias)
{
CreateAndSaveMediaXml(media.ToXml(), media.Id, uow.Database);
}
}
foreach (var media in medias)
{
CreateAndSaveMediaXml(media.ToXml(), media.Id, uow.Database);
}
}
if (raiseEvents)
Saved.RaiseEvent(new SaveEventArgs<IMedia>(medias, false), this);
if(raiseEvents)
Saved.RaiseEvent(new SaveEventArgs<IMedia>(medias, false), this);
Audit.Add(AuditTypes.Save, "Save Media items performed by user", userId, -1);
Audit.Add(AuditTypes.Save, "Save Media items performed by user", userId, -1);
}
}
/// <summary>

View File

@@ -1,7 +1,11 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Xml.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -17,6 +21,8 @@ namespace Umbraco.Core.Services
private readonly RepositoryFactory _repositoryFactory;
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
public MemberService(RepositoryFactory repositoryFactory)
: this(new PetaPocoUnitOfWorkProvider(), repositoryFactory)
{
@@ -108,6 +114,22 @@ namespace Umbraco.Core.Services
}
}
/// <summary>
/// Gets a list of Members by their MemberType
/// </summary>
/// <param name="memberTypeId"></param>
/// <returns></returns>
public IEnumerable<IMember> GetMembersByMemberType(int memberTypeId)
{
using (var repository = _repositoryFactory.CreateMemberRepository(_uowProvider.GetUnitOfWork()))
{
repository.Get(memberTypeId);
var query = Query<IMember>.Builder.Where(x => x.ContentTypeId == memberTypeId);
var members = repository.GetByQuery(query);
return members;
}
}
/// <summary>
/// Gets a list of Members by the MemberGroup they are part of
/// </summary>
@@ -134,20 +156,88 @@ namespace Umbraco.Core.Services
}
}
public void DeleteMembersOfType(int memberTypeId)
{
using (new WriteLock(Locker))
{
using (var uow = _uowProvider.GetUnitOfWork())
{
var repository = _repositoryFactory.CreateMemberRepository(uow);
//NOTE What about content that has the contenttype as part of its composition?
var query = Query<IMember>.Builder.Where(x => x.ContentTypeId == memberTypeId);
var members = repository.GetByQuery(query).ToArray();
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IMember>(members), this))
return;
foreach (var member in members)
{
//Permantly delete the member
Delete(member);
}
}
}
}
/// <summary>
/// Does a search for members that contain the specified string in their email address
/// </summary>
/// <param name="emailStringToMatch"></param>
/// <param name="matchType"></param>
/// <returns></returns>
public IEnumerable<IMember> FindMembersByEmail(string emailStringToMatch)
public IEnumerable<IMember> FindMembersByEmail(string emailStringToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
{
var query = new Query<IMember>();
switch (matchType)
{
case StringPropertyMatchType.Exact:
query.Where(member => member.Email.Equals(emailStringToMatch));
break;
case StringPropertyMatchType.Contains:
query.Where(member => member.Email.Contains(emailStringToMatch));
break;
case StringPropertyMatchType.StartsWith:
query.Where(member => member.Email.StartsWith(emailStringToMatch));
break;
case StringPropertyMatchType.EndsWith:
query.Where(member => member.Email.EndsWith(emailStringToMatch));
break;
default:
throw new ArgumentOutOfRangeException("matchType");
}
query.Where(member => member.Email.Contains(emailStringToMatch));
return repository.GetByQuery(query);
}
}
public IEnumerable<IMember> FindMembersByUsername(string login, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
{
var query = new Query<IMember>();
switch (matchType)
{
case StringPropertyMatchType.Exact:
query.Where(member => member.Username.Equals(login));
break;
case StringPropertyMatchType.Contains:
query.Where(member => member.Username.Contains(login));
break;
case StringPropertyMatchType.StartsWith:
query.Where(member => member.Username.StartsWith(login));
break;
case StringPropertyMatchType.EndsWith:
query.Where(member => member.Username.EndsWith(login));
break;
default:
throw new ArgumentOutOfRangeException("matchType");
}
return repository.GetByQuery(query);
}
@@ -158,17 +248,51 @@ namespace Umbraco.Core.Services
/// </summary>
/// <param name="propertyTypeAlias"></param>
/// <param name="value"></param>
/// <param name="matchType"></param>
/// <returns></returns>
public IEnumerable<IMember> GetMembersByPropertyValue(string propertyTypeAlias, string value)
public IEnumerable<IMember> GetMembersByPropertyValue(string propertyTypeAlias, string value, StringPropertyMatchType matchType = StringPropertyMatchType.Exact)
{
using (var repository = _repositoryFactory.CreateMemberRepository(_uowProvider.GetUnitOfWork()))
{
var query =
Query<IMember>.Builder.Where(
x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
(((Member)x).LongStringPropertyValue.Contains(value) ||
((Member)x).ShortStringPropertyValue.Contains(value)));
IQuery<IMember> query;
switch (matchType)
{
case StringPropertyMatchType.Exact:
query =
Query<IMember>.Builder.Where(
x =>
((Member) x).PropertyTypeAlias == propertyTypeAlias &&
(((Member)x).LongStringPropertyValue.SqlEquals(value, TextColumnType.NText) ||
((Member)x).ShortStringPropertyValue.SqlEquals(value, TextColumnType.NVarchar)));
break;
case StringPropertyMatchType.Contains:
query =
Query<IMember>.Builder.Where(
x =>
((Member) x).PropertyTypeAlias == propertyTypeAlias &&
(((Member)x).LongStringPropertyValue.SqlContains(value, TextColumnType.NText) ||
((Member)x).ShortStringPropertyValue.SqlContains(value, TextColumnType.NVarchar)));
break;
case StringPropertyMatchType.StartsWith:
query =
Query<IMember>.Builder.Where(
x =>
((Member) x).PropertyTypeAlias == propertyTypeAlias &&
(((Member)x).LongStringPropertyValue.SqlStartsWith(value, TextColumnType.NText) ||
((Member)x).ShortStringPropertyValue.SqlStartsWith(value, TextColumnType.NVarchar)));
break;
case StringPropertyMatchType.EndsWith:
query =
Query<IMember>.Builder.Where(
x =>
((Member) x).PropertyTypeAlias == propertyTypeAlias &&
(((Member)x).LongStringPropertyValue.SqlEndsWith(value, TextColumnType.NText) ||
((Member)x).ShortStringPropertyValue.SqlEndsWith(value, TextColumnType.NVarchar)));
break;
default:
throw new ArgumentOutOfRangeException("matchType");
}
var members = repository.GetByQuery(query);
return members;
@@ -243,15 +367,14 @@ namespace Umbraco.Core.Services
#region IMembershipMemberService Implementation
/// <summary>
/// Creates a new Member
/// Creates and persists a new Member
/// </summary>
/// <param name="email"></param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="memberTypeAlias"></param>
/// <param name="userId"></param>
/// <returns></returns>
public IMember CreateMember(string email, string username, string password, string memberTypeAlias, int userId = 0)
public IMember CreateMember(string email, string username, string password, string memberTypeAlias)
{
var uow = _uowProvider.GetUnitOfWork();
IMemberType memberType;
@@ -308,7 +431,7 @@ namespace Umbraco.Core.Services
{
using (var repository = _repositoryFactory.CreateMemberRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IMember>.Builder.Where(x => x.Email == email);
var query = Query<IMember>.Builder.Where(x => x.Email.Equals(email));
var member = repository.GetByQuery(query).FirstOrDefault();
return member;
@@ -337,28 +460,239 @@ namespace Umbraco.Core.Services
/// <param name="member"></param>
public void Delete(IMember member)
{
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IMember>(member), this))
return;
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
{
repository.Delete(member);
uow.Commit();
}
Deleted.RaiseEvent(new DeleteEventArgs<IMember>(member, false), this);
}
/// <summary>
/// Saves an updated Member
/// </summary>
/// <param name="member"></param>
public void Save(IMember member)
/// <param name="raiseEvents"></param>
public void Save(IMember member, bool raiseEvents = true)
{
if (raiseEvents)
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMember>(member), this))
return;
}
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
{
repository.AddOrUpdate(member);
uow.Commit();
var xml = member.ToXml();
CreateAndSaveMemberXml(xml, member.Id, uow.Database);
}
if (raiseEvents)
Saved.RaiseEvent(new SaveEventArgs<IMember>(member, false), this);
}
public void Save(IEnumerable<IMember> members, bool raiseEvents = true)
{
if (raiseEvents)
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMember>(members), this))
return;
}
using (new WriteLock(Locker))
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
{
foreach (var member in members)
{
repository.AddOrUpdate(member);
}
//commit the whole lot in one go
uow.Commit();
foreach (var member in members)
{
CreateAndSaveMemberXml(member.ToXml(), member.Id, uow.Database);
}
}
if (raiseEvents)
Saved.RaiseEvent(new SaveEventArgs<IMember>(members, false), this);
}
}
#endregion
/// <summary>
/// Rebuilds all xml content in the cmsContentXml table for all media
/// </summary>
/// <param name="memberTypeIds">
/// Only rebuild the xml structures for the content type ids passed in, if none then rebuilds the structures
/// for all members = USE WITH CARE!
/// </param>
/// <returns>True if publishing succeeded, otherwise False</returns>
internal void RebuildXmlStructures(params int[] memberTypeIds)
{
using (new WriteLock(Locker))
{
var list = new List<IMember>();
var uow = _uowProvider.GetUnitOfWork();
//First we're going to get the data that needs to be inserted before clearing anything, this
//ensures that we don't accidentally leave the content xml table empty if something happens
//during the lookup process.
if (memberTypeIds.Any() == false)
{
list.AddRange(GetAllMembers());
}
else
{
list.AddRange(memberTypeIds.SelectMany(GetMembersByMemberType));
}
var xmlItems = new List<ContentXmlDto>();
foreach (var c in list)
{
var xml = c.ToXml();
xmlItems.Add(new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) });
}
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
using (var tr = uow.Database.GetTransaction())
{
if (memberTypeIds.Any() == false)
{
//Remove all media records from the cmsContentXml table (DO NOT REMOVE Content/Members!)
uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
INNER JOIN umbracoNode ON cmsContentXml.nodeId = umbracoNode.id
WHERE nodeObjectType = @nodeObjectType)",
new { nodeObjectType = Constants.ObjectTypes.Member });
}
else
{
foreach (var id in memberTypeIds)
{
//first we'll clear out the data from the cmsContentXml table for this type
uow.Database.Execute(@"delete from cmsContentXml where nodeId in
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
INNER JOIN umbracoNode ON cmsContentXml.nodeId = umbracoNode.id
INNER JOIN cmsContent ON cmsContent.nodeId = umbracoNode.id
WHERE nodeObjectType = @nodeObjectType AND cmsContent.contentType = @contentTypeId)",
new { contentTypeId = id, nodeObjectType = Constants.ObjectTypes.Member });
}
}
//bulk insert it into the database
uow.Database.BulkInsertRecords(xmlItems, tr);
}
}
}
private void CreateAndSaveMemberXml(XElement xml, int id, UmbracoDatabase db)
{
var poco = new ContentXmlDto { NodeId = id, Xml = xml.ToString(SaveOptions.None) };
var exists = db.FirstOrDefault<ContentXmlDto>("WHERE nodeId = @Id", new { Id = id }) != null;
int result = exists ? db.Update(poco) : Convert.ToInt32(db.Insert(poco));
}
#region Event Handlers
/// <summary>
/// Occurs before Delete
/// </summary>
public static event TypedEventHandler<IMemberService, DeleteEventArgs<IMember>> Deleting;
/// <summary>
/// Occurs after Delete
/// </summary>
public static event TypedEventHandler<IMemberService, DeleteEventArgs<IMember>> Deleted;
/// <summary>
/// Occurs before Save
/// </summary>
public static event TypedEventHandler<IMemberService, SaveEventArgs<IMember>> Saving;
/// <summary>
/// Occurs after Save
/// </summary>
public static event TypedEventHandler<IMemberService, SaveEventArgs<IMember>> Saved;
#endregion
///// <summary>
///// A helper method that will create a basic/generic member for use with a generic membership provider
///// </summary>
///// <returns></returns>
//internal static IMember CreateGenericMembershipProviderMember(string name, string email, string username, string password)
//{
// var identity = int.MaxValue;
// var memType = new MemberType(-1);
// var propGroup = new PropertyGroup
// {
// Name = "Membership",
// Id = --identity
// };
// propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
// {
// Alias = Constants.Conventions.Member.Comments,
// Name = Constants.Conventions.Member.CommentsLabel,
// SortOrder = 0,
// Id = --identity
// });
// propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer)
// {
// Alias = Constants.Conventions.Member.IsApproved,
// Name = Constants.Conventions.Member.IsApprovedLabel,
// SortOrder = 3,
// Id = --identity
// });
// propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer)
// {
// Alias = Constants.Conventions.Member.IsLockedOut,
// Name = Constants.Conventions.Member.IsLockedOutLabel,
// SortOrder = 4,
// Id = --identity
// });
// propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date)
// {
// Alias = Constants.Conventions.Member.LastLockoutDate,
// Name = Constants.Conventions.Member.LastLockoutDateLabel,
// SortOrder = 5,
// Id = --identity
// });
// propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date)
// {
// Alias = Constants.Conventions.Member.LastLoginDate,
// Name = Constants.Conventions.Member.LastLoginDateLabel,
// SortOrder = 6,
// Id = --identity
// });
// propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date)
// {
// Alias = Constants.Conventions.Member.LastPasswordChangeDate,
// Name = Constants.Conventions.Member.LastPasswordChangeDateLabel,
// SortOrder = 7,
// Id = --identity
// });
// memType.PropertyGroups.Add(propGroup);
// return new Member(name, email, username, password, -1, memType);
//}
}
}

View File

@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Umbraco.Core.Auditing;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
@@ -8,25 +11,29 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
{
internal class MemberTypeService : IMemberTypeService
internal class MemberTypeService : ContentTypeServiceBase, IMemberTypeService
{
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private readonly RepositoryFactory _repositoryFactory;
private readonly IMemberService _memberService;
public MemberTypeService()
: this(new PetaPocoUnitOfWorkProvider(), new RepositoryFactory())
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
public MemberTypeService(IMemberService memberService)
: this(new PetaPocoUnitOfWorkProvider(), new RepositoryFactory(), memberService)
{ }
public MemberTypeService(RepositoryFactory repositoryFactory)
: this(new PetaPocoUnitOfWorkProvider(), repositoryFactory)
public MemberTypeService(RepositoryFactory repositoryFactory, IMemberService memberService)
: this(new PetaPocoUnitOfWorkProvider(), repositoryFactory, memberService)
{ }
public MemberTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory)
public MemberTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, IMemberService memberService)
{
if (provider == null) throw new ArgumentNullException("provider");
if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory");
_uowProvider = provider;
_repositoryFactory = repositoryFactory;
_memberService = memberService;
}
public IEnumerable<IMemberType> GetAllMemberTypes(params int[] ids)
@@ -66,5 +73,143 @@ namespace Umbraco.Core.Services
}
}
public void Save(IMemberType memberType, int userId = 0)
{
if (SavingMemberType.IsRaisedEventCancelled(new SaveEventArgs<IMemberType>(memberType), this))
return;
using (new WriteLock(Locker))
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberTypeRepository(uow))
{
memberType.CreatorId = userId;
repository.AddOrUpdate(memberType);
uow.Commit();
}
UpdateContentXmlStructure(memberType);
}
SavedMemberType.RaiseEvent(new SaveEventArgs<IMemberType>(memberType, false), this);
}
public void Save(IEnumerable<IMemberType> memberTypes, int userId = 0)
{
var asArray = memberTypes.ToArray();
if (SavingMemberType.IsRaisedEventCancelled(new SaveEventArgs<IMemberType>(asArray), this))
return;
using (new WriteLock(Locker))
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberTypeRepository(uow))
{
foreach (var memberType in asArray)
{
memberType.CreatorId = userId;
repository.AddOrUpdate(memberType);
}
//save it all in one go
uow.Commit();
}
UpdateContentXmlStructure(asArray.Cast<IContentTypeBase>().ToArray());
}
SavedMemberType.RaiseEvent(new SaveEventArgs<IMemberType>(asArray, false), this);
}
public void Delete(IMemberType memberType, int userId = 0)
{
if (DeletingMemberType.IsRaisedEventCancelled(new DeleteEventArgs<IMemberType>(memberType), this))
return;
using (new WriteLock(Locker))
{
_memberService.DeleteMembersOfType(memberType.Id);
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberTypeRepository(uow))
{
repository.Delete(memberType);
uow.Commit();
DeletedMemberType.RaiseEvent(new DeleteEventArgs<IMemberType>(memberType, false), this);
}
}
}
public void Delete(IEnumerable<IMemberType> memberTypes, int userId = 0)
{
var asArray = memberTypes.ToArray();
if (DeletingMemberType.IsRaisedEventCancelled(new DeleteEventArgs<IMemberType>(asArray), this))
return;
using (new WriteLock(Locker))
{
foreach (var contentType in asArray)
{
_memberService.DeleteMembersOfType(contentType.Id);
}
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberTypeRepository(uow))
{
foreach (var memberType in asArray)
{
repository.Delete(memberType);
}
uow.Commit();
DeletedMemberType.RaiseEvent(new DeleteEventArgs<IMemberType>(asArray, false), this);
}
}
}
/// <summary>
/// This is called after an IContentType is saved and is used to update the content xml structures in the database
/// if they are required to be updated.
/// </summary>
/// <param name="contentTypes">A tuple of a content type and a boolean indicating if it is new (HasIdentity was false before committing)</param>
private void UpdateContentXmlStructure(params IContentTypeBase[] contentTypes)
{
var toUpdate = GetContentTypesForXmlUpdates(contentTypes).ToArray();
if (toUpdate.Any())
{
//if it is a media type then call the rebuilding methods for media
var typedMemberService = _memberService as MemberService;
if (typedMemberService != null)
{
typedMemberService.RebuildXmlStructures(toUpdate.Select(x => x.Id).ToArray());
}
}
}
/// <summary>
/// Occurs before Save
/// </summary>
public static event TypedEventHandler<IMemberTypeService, SaveEventArgs<IMemberType>> SavingMemberType;
/// <summary>
/// Occurs after Save
/// </summary>
public static event TypedEventHandler<IMemberTypeService, SaveEventArgs<IMemberType>> SavedMemberType;
/// <summary>
/// Occurs before Delete
/// </summary>
public static event TypedEventHandler<IMemberTypeService, DeleteEventArgs<IMemberType>> DeletingMemberType;
/// <summary>
/// Occurs after Delete
/// </summary>
public static event TypedEventHandler<IMemberTypeService, DeleteEventArgs<IMemberType>> DeletedMemberType;
}
}

View File

@@ -144,7 +144,7 @@ namespace Umbraco.Core.Services
// _macroService = new Lazy<IMacroService>(() => new MacroService(provider, repositoryFactory.Value));
if (_memberTypeService == null)
_memberTypeService = new Lazy<IMemberTypeService>(() => new MemberTypeService(provider, repositoryFactory.Value));
_memberTypeService = new Lazy<IMemberTypeService>(() => new MemberTypeService(provider, repositoryFactory.Value, _memberService.Value));
}

View File

@@ -465,6 +465,11 @@ namespace Umbraco.Core
return compare.StartsWith(compareTo, StringComparison.InvariantCultureIgnoreCase);
}
public static bool InvariantEndsWith(this string compare, string compareTo)
{
return compare.EndsWith(compareTo, StringComparison.InvariantCultureIgnoreCase);
}
public static bool InvariantContains(this string compare, string compareTo)
{
return compare.IndexOf(compareTo, StringComparison.OrdinalIgnoreCase) >= 0;

View File

@@ -571,7 +571,9 @@ function validateSafeAlias(id, value, immediate, callback) {{
for (var i = 0; i < ilen; i++)
{
var c = input[i];
var isTerm = config.IsTerm(c, opos == 0);
// leading as long as StateBreak and ipos still zero
var leading = state == StateBreak && ipos == 0;
var isTerm = config.IsTerm(c, leading);
//var isDigit = char.IsDigit(c);
var isUpper = char.IsUpper(c); // false for digits, symbols...
@@ -583,6 +585,12 @@ function validateSafeAlias(id, value, immediate, callback) {{
if (isPair)
throw new NotSupportedException("Surrogate pairs are not supported.");
Console.WriteLine("CHAR '{0}' {1} {2} - {3} - {4}/{5} {6}",
c,
isTerm ? "term" : "!term", isUpper ? "upper" : "!upper",
state,
i, ipos, leading ? "leading" : "!leading");
switch (state)
{
// within a break
@@ -689,10 +697,10 @@ function validateSafeAlias(id, value, immediate, callback) {{
CleanStringType caseType, CultureInfo culture, bool isAcronym)
{
var term = input.Substring(ipos, len);
//Console.WriteLine("TERM \"{0}\" {1} {2}",
// term,
// isAcronym ? "acronym" : "word",
// caseType);
Console.WriteLine("TERM \"{0}\" {1} {2}",
term,
isAcronym ? "acronym" : "word",
caseType);
if (isAcronym)
{

View File

@@ -200,6 +200,13 @@
<Compile Include="Models\PublishedContent\PublishedContentModel.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryResolver.cs" />
<Compile Include="Models\TemplateNode.cs" />
<Compile Include="Persistence\Querying\SqlStringExtensions.cs" />
<Compile Include="Persistence\Querying\StringPropertyMatchType.cs" />
<Compile Include="Persistence\Querying\TextColumnType.cs" />
<Compile Include="Persistence\SqlSyntax\MySqlSyntax.cs" />
<Compile Include="Persistence\SqlSyntax\SqlCeSyntax.cs" />
<Compile Include="Persistence\SqlSyntax\SqlServerSyntax.cs" />
<Compile Include="Persistence\SqlSyntax\SqlServerVersionName.cs" />
<Compile Include="PropertyEditors\PropertyCacheValue.cs" />
<Compile Include="PropertyEditors\PropertyValueCacheAttribute.cs" />
<Compile Include="PropertyEditors\PropertyValueTypeAttribute.cs" />
@@ -745,6 +752,7 @@
<Compile Include="Serialization\StreamedResult.cs" />
<Compile Include="Services\ContentService.cs" />
<Compile Include="Services\ContentTypeService.cs" />
<Compile Include="Services\ContentTypeServiceBase.cs" />
<Compile Include="Services\DataTypeService.cs" />
<Compile Include="Services\EntityService.cs" />
<Compile Include="Services\FileService.cs" />
@@ -756,6 +764,7 @@
<Compile Include="Services\ILocalizationService.cs" />
<Compile Include="Services\IMediaService.cs" />
<Compile Include="Services\IMemberService.cs" />
<Compile Include="Services\IMembershipMemberService.cs" />
<Compile Include="Services\IMembershipUserService.cs" />
<Compile Include="Services\IMemberTypeService.cs" />
<Compile Include="Services\IRelationService.cs" />