Compare commits

..

3 Commits

158 changed files with 1772 additions and 5503 deletions
+5 -5
View File
@@ -28,17 +28,17 @@
<dependency id="MySql.Data" version="[6.9.8, 7.0.0)" />
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
<!-- AutoMapper can not be updated due to: https://github.com/AutoMapper/AutoMapper/issues/373#issuecomment-127644405 -->
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
<dependency id="Examine" version="[0.1.81, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.2, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.2, 5.0.0)" />
<dependency id="Examine" version="[0.1.70, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.1, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.7.2, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 2.0.0)" />
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
<dependency id="Markdown" version="[1.14.4, 2.0.0)" />
</dependencies>
</metadata>
<files>
-1
View File
@@ -45,7 +45,6 @@
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
<file src="tools\processing.config.install.xdt" target="Content\Config\imageprocessor\processing.config.install.xdt" />
<file src="tools\cache.config.install.xdt" target="Content\Config\imageprocessor\cache.config.install.xdt" />
<file src="build\**" target="build" />
</files>
</package>
@@ -1,6 +0,0 @@
<?xml version="1.0"?>
<caching xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<caches>
<cache name="DiskCache" trimCache="false" xdt:Transform="SetAttributes(trimCache)" xdt:Locator="Match(name)" />
</caches>
</caching>
+1 -1
View File
@@ -1,2 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.5.11
7.5.7
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.5.11")]
[assembly: AssemblyInformationalVersion("7.5.11")]
[assembly: AssemblyFileVersion("7.5.7")]
[assembly: AssemblyInformationalVersion("7.5.7")]
+1 -2
View File
@@ -64,8 +64,7 @@ namespace Umbraco.Core.Cache
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:";
[Obsolete("No longer used and will be removed in v8")]
[EditorBrowsable(EditorBrowsableState.Never)]
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string PropertyTypeCacheKey = "UmbracoPropertyTypeCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
@@ -1,9 +0,0 @@
namespace Umbraco.Core.Configuration
{
internal enum ContentXmlStorage
{
Default,
AspNetTemp,
EnvironmentTemp
}
}
@@ -520,25 +520,12 @@ namespace Umbraco.Core.Configuration
}
internal static bool ContentCacheXmlStoredInCodeGen
{
get { return ContentCacheXmlStorageLocation == ContentXmlStorage.AspNetTemp; }
}
internal static ContentXmlStorage ContentCacheXmlStorageLocation
{
get
{
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLStorage"))
{
return Enum<ContentXmlStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
}
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLUseLocalTemp"))
{
return bool.Parse(ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"])
? ContentXmlStorage.AspNetTemp
: ContentXmlStorage.Default;
}
return ContentXmlStorage.Default;
//defaults to false
return ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLUseLocalTemp")
&& bool.Parse(ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"]); //default to false
}
}
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.5.11");
private static readonly Version Version = new Version("7.5.7");
/// <summary>
/// Gets the current version of Umbraco.
+1 -1
View File
@@ -415,7 +415,7 @@ namespace Umbraco.Core
if (currentTry == 5)
{
throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but Umbraco cannot connect to the database.");
throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but the Umbraco cannot connect to the database.");
}
}
+9 -4
View File
@@ -203,10 +203,15 @@ namespace Umbraco.Core
var path = Path.Combine(GlobalSettings.FullpathToRoot, "App_Data", "Umbraco.sdf");
if (File.Exists(path) == false)
{
using (var engine = new SqlCeEngine(connectionString))
{
engine.CreateDatabase();
}
var engine = new SqlCeEngine(connectionString);
engine.CreateDatabase();
// SD: Pretty sure this should be in a using clause but i don't want to cause unknown side-effects here
// since it's been like this for quite some time
//using (var engine = new SqlCeEngine(connectionString))
//{
// engine.CreateDatabase();
//}
}
Initialize(providerName);
+4 -18
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.IO;
using System.Linq;
@@ -73,28 +72,15 @@ namespace Umbraco.Core.IO
{
get
{
switch (GlobalSettings.ContentCacheXmlStorageLocation)
{
case ContentXmlStorage.AspNetTemp:
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
case ContentXmlStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoXml",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
appDomainHash);
return Path.Combine(cachePath, "umbraco.config");
case ContentXmlStorage.Default:
return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config");
default:
throw new ArgumentOutOfRangeException();
if (GlobalSettings.ContentCacheXmlStoredInCodeGen && SystemUtilities.GetCurrentTrustLevel() == AspNetHostingPermissionLevel.Unrestricted)
{
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
}
return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config");
}
}
[Obsolete("Use GlobalSettings.ContentCacheXmlStoredInCodeGen instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
internal static bool ContentCacheXmlStoredInCodeGen
{
get { return GlobalSettings.ContentCacheXmlStoredInCodeGen; }
+1 -1
View File
@@ -4,7 +4,7 @@ namespace Umbraco.Core.Models
{
/// <summary>
/// Defines a ContentType, which Media is based on
/// </summary>
/// </summary
public interface IMediaType : IContentTypeComposition
{
@@ -13,7 +13,6 @@ namespace Umbraco.Core.Models.Identity
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
{
config.CreateMap<IUser, BackOfficeIdentityUser>()
.ForMember(user => user.LastLoginDateUtc, expression => expression.MapFrom(user => user.LastLoginDate.ToUniversalTime()))
.ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email))
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
.ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null))
@@ -24,17 +24,12 @@ namespace Umbraco.Core.Models.Identity
///
/// </summary>
public IdentityUser()
{
{
this.Claims = new List<TClaim>();
this.Roles = new List<TRole>();
this.Logins = new List<TLogin>();
}
/// <summary>
/// Last login date
/// </summary>
public virtual DateTime? LastLoginDateUtc { get; set; }
/// <summary>
/// Email
///
@@ -69,7 +69,6 @@ namespace Umbraco.Core.Models
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
//TODO: Instead of 'new' this should explicitly implement one of the collection interfaces members
internal new void Add(PropertyType item)
{
using (new WriteLock(_addLocker))
@@ -110,12 +110,6 @@ namespace Umbraco.Core.Models
_ruleCollection.Clear();
}
internal void ClearRemovedRules()
{
_removedRules.Clear();
}
[DataMember]
public int LoginNodeId
{
@@ -106,13 +106,12 @@ namespace Umbraco.Core.Models.PublishedContent
/// <param name="propertyTypeAlias">The property type alias.</param>
/// <param name="dataTypeDefinitionId">The datatype definition identifier.</param>
/// <param name="propertyEditorAlias">The property editor alias.</param>
/// <param name="initConverters">Generally used only for testing, in production this will always be true</param>
/// <remarks>
/// <para>The new published property type does not belong to a published content type.</para>
/// <para>The values of <paramref name="dataTypeDefinitionId"/> and <paramref name="propertyEditorAlias"/> are
/// assumed to be valid and consistent.</para>
/// </remarks>
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias, bool initConverters = true)
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias)
{
// ContentType
// - in unit tests, to be set by PublishedContentType when creating it
@@ -123,8 +122,7 @@ namespace Umbraco.Core.Models.PublishedContent
DataTypeId = dataTypeDefinitionId;
PropertyEditorAlias = propertyEditorAlias;
if (initConverters)
InitializeConverters();
InitializeConverters();
}
#endregion
@@ -27,28 +27,15 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory<IContent,DocumentDto>
/// <summary>
/// Builds a IContent item from the dto(s) and content type
/// </summary>
/// <param name="dto">
/// This DTO can contain all of the information to build an IContent item, however in cases where multiple entities are being built,
/// a separate <see cref="DocumentPublishedReadOnlyDto"/> publishedDto entity will be supplied in place of the <see cref="DocumentDto"/>'s own
/// ResultColumn DocumentPublishedReadOnlyDto
/// </param>
/// <param name="contentType"></param>
/// <param name="publishedDto">
/// When querying for multiple content items the main DTO will not contain the ResultColumn DocumentPublishedReadOnlyDto and a separate publishedDto instance will be supplied
/// </param>
/// <returns></returns>
public static IContent BuildEntity(DocumentDto dto, IContentType contentType, DocumentPublishedReadOnlyDto publishedDto = null)
public IContent BuildEntity(DocumentDto dto)
{
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, contentType);
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, _contentType);
try
{
content.DisableChangeTracking();
content.Id = dto.NodeId;
content.Id = _id;
content.Key = dto.ContentVersionDto.ContentDto.NodeDto.UniqueId;
content.Name = dto.Text;
content.NodeName = dto.ContentVersionDto.ContentDto.NodeDto.Text;
@@ -62,16 +49,11 @@ namespace Umbraco.Core.Persistence.Factories
content.Published = dto.Published;
content.CreateDate = dto.ContentVersionDto.ContentDto.NodeDto.CreateDate;
content.UpdateDate = dto.ContentVersionDto.VersionDate;
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?)null;
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?)null;
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?) null;
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?) null;
content.Version = dto.ContentVersionDto.VersionId;
content.PublishedState = dto.Published ? PublishedState.Published : PublishedState.Unpublished;
//Check if the publishedDto has been supplied, if not the use the dto's own DocumentPublishedReadOnlyDto value
content.PublishedVersionGuid = publishedDto == null
? (dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId)
: publishedDto.VersionId;
content.PublishedVersionGuid = dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId;
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -82,13 +64,6 @@ namespace Umbraco.Core.Persistence.Factories
{
content.EnableChangeTracking();
}
}
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
public IContent BuildEntity(DocumentDto dto)
{
return BuildEntity(dto, _contentType);
}
public DocumentDto BuildDto(IContent entity)
@@ -27,15 +27,15 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory<IMedia,ContentVersionDto>
public static IMedia BuildEntity(ContentVersionDto dto, IMediaType contentType)
public IMedia BuildEntity(ContentVersionDto dto)
{
var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, contentType);
var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, _contentType);
try
{
media.DisableChangeTracking();
media.Id = dto.NodeId;
media.Id = _id;
media.Key = dto.ContentDto.NodeDto.UniqueId;
media.Path = dto.ContentDto.NodeDto.Path;
media.CreatorId = dto.ContentDto.NodeDto.UserId.Value;
@@ -55,13 +55,6 @@ namespace Umbraco.Core.Persistence.Factories
{
media.EnableChangeTracking();
}
}
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
public IMedia BuildEntity(ContentVersionDto dto)
{
return BuildEntity(dto, _contentType);
}
public ContentVersionDto BuildDto(IMedia entity)
@@ -28,17 +28,17 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory<IMedia,ContentVersionDto>
public static IMember BuildEntity(MemberDto dto, IMemberType contentType)
public IMember BuildEntity(MemberDto dto)
{
var member = new Member(
dto.ContentVersionDto.ContentDto.NodeDto.Text,
dto.Email, dto.LoginName, dto.Password, contentType);
dto.Email, dto.LoginName, dto.Password, _contentType);
try
{
member.DisableChangeTracking();
member.Id = dto.NodeId;
member.Id = _id;
member.Key = dto.ContentVersionDto.ContentDto.NodeDto.UniqueId;
member.Path = dto.ContentVersionDto.ContentDto.NodeDto.Path;
member.CreatorId = dto.ContentVersionDto.ContentDto.NodeDto.UserId.Value;
@@ -62,12 +62,6 @@ namespace Umbraco.Core.Persistence.Factories
}
}
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
public IMember BuildEntity(MemberDto dto)
{
return BuildEntity(dto, _contentType);
}
public MemberDto BuildDto(IMember entity)
{
var dto = new MemberDto
@@ -30,11 +30,11 @@ namespace Umbraco.Core.Persistence.Factories
_updateDate = updateDate;
}
public static IEnumerable<Property> BuildEntity(IReadOnlyCollection<PropertyDataDto> dtos, PropertyType[] compositionTypeProperties, DateTime createDate, DateTime updateDate)
public IEnumerable<Property> BuildEntity(PropertyDataDto[] dtos)
{
var properties = new List<Property>();
foreach (var propertyType in compositionTypeProperties)
foreach (var propertyType in _compositionTypeProperties)
{
var propertyDataDto = dtos.LastOrDefault(x => x.PropertyTypeId == propertyType.Id);
var property = propertyDataDto == null
@@ -47,8 +47,8 @@ namespace Umbraco.Core.Persistence.Factories
//on initial construction we don't want to have dirty properties tracked
property.DisableChangeTracking();
property.CreateDate = createDate;
property.UpdateDate = updateDate;
property.CreateDate = _createDate;
property.UpdateDate = _updateDate;
// http://issues.umbraco.org/issue/U4-1946
property.ResetDirtyProperties(false);
properties.Add(property);
@@ -57,18 +57,12 @@ namespace Umbraco.Core.Persistence.Factories
{
property.EnableChangeTracking();
}
}
return properties;
}
[Obsolete("Use the static method instead, there's no reason to allocate one of these classes everytime we want to map values")]
public IEnumerable<Property> BuildEntity(PropertyDataDto[] dtos)
{
return BuildEntity(dtos, _compositionTypeProperties, _createDate, _updateDate);
}
public IEnumerable<PropertyDataDto> BuildDto(IEnumerable<Property> properties)
{
var propertyDataDtos = new List<PropertyDataDto>();
@@ -6,7 +6,6 @@ using System.Reflection;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Persistence.Factories
{
@@ -18,7 +17,7 @@ namespace Umbraco.Core.Persistence.Factories
//figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data
foreach (var k in originalEntityProperties.Keys
.Select(x => new { orig = x, title = x.ToCleanString(CleanStringType.PascalCase | CleanStringType.Ascii | CleanStringType.ConvertCase) })
.Select(x => new { orig = x, title = x.ConvertCase(StringAliasCaseType.PascalCase) })
.Where(x => entityProps.InvariantContains(x.title) == false))
{
entity.AdditionalData[k.title] = originalEntityProperties[k.orig];
@@ -76,6 +75,65 @@ namespace Umbraco.Core.Persistence.Factories
entity.EnableChangeTracking();
}
}
public UmbracoEntity BuildEntity(EntityRepository.UmbracoEntityDto dto)
{
var entity = new UmbracoEntity(dto.Trashed)
{
CreateDate = dto.CreateDate,
CreatorId = dto.UserId.Value,
Id = dto.NodeId,
Key = dto.UniqueId,
Level = dto.Level,
Name = dto.Text,
NodeObjectTypeId = dto.NodeObjectType.Value,
ParentId = dto.ParentId,
Path = dto.Path,
SortOrder = dto.SortOrder,
HasChildren = dto.Children > 0,
ContentTypeAlias = dto.Alias ?? string.Empty,
ContentTypeIcon = dto.Icon ?? string.Empty,
ContentTypeThumbnail = dto.Thumbnail ?? string.Empty,
};
entity.IsPublished = dto.PublishedVersion != default(Guid) || (dto.NewestVersion != default(Guid) && dto.PublishedVersion == dto.NewestVersion);
entity.IsDraft = dto.NewestVersion != default(Guid) && (dto.PublishedVersion == default(Guid) || dto.PublishedVersion != dto.NewestVersion);
entity.HasPendingChanges = (dto.PublishedVersion != default(Guid) && dto.NewestVersion != default(Guid)) && dto.PublishedVersion != dto.NewestVersion;
if (dto.UmbracoPropertyDtos != null)
{
foreach (var propertyDto in dto.UmbracoPropertyDtos)
{
entity.AdditionalData[propertyDto.PropertyAlias] = new UmbracoEntity.EntityProperty
{
PropertyEditorAlias = propertyDto.PropertyEditorAlias,
Value = propertyDto.NTextValue.IsNullOrWhiteSpace()
? propertyDto.NVarcharValue
: propertyDto.NTextValue.ConvertToJsonIfPossible()
};
}
}
return entity;
}
public EntityRepository.UmbracoEntityDto BuildDto(UmbracoEntity entity)
{
var node = new EntityRepository.UmbracoEntityDto
{
CreateDate = entity.CreateDate,
Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
NodeId = entity.Id,
NodeObjectType = entity.NodeObjectTypeId,
ParentId = entity.ParentId,
Path = entity.Path,
SortOrder = entity.SortOrder,
Text = entity.Name,
Trashed = entity.Trashed,
UniqueId = entity.Key,
UserId = entity.CreatorId
};
return node;
}
}
}
@@ -31,7 +31,7 @@ namespace Umbraco.Core.Persistence.Mappers
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public virtual BaseMapper ResolveMapperByType(Type type)
internal BaseMapper ResolveMapperByType(Type type)
{
return _mapperCache.GetOrAdd(type, type1 =>
{
@@ -67,7 +67,7 @@ namespace Umbraco.Core.Persistence.Mappers
return Attempt<BaseMapper>.Succeed(mapper);
}
public virtual string GetMapping(Type type, string propertyName)
internal string GetMapping(Type type, string propertyName)
{
var mapper = ResolveMapperByType(type);
var result = mapper.Map(propertyName);
@@ -581,18 +581,6 @@ namespace Umbraco.Core.Persistence.Querying
case "InvariantContains":
case "InvariantEquals":
//special case, if it is 'Contains' and the argument that Contains is being called on is
//Enumerable and the methodArgs is the actual member access, then it's an SQL IN clause
if (m.Object == null
&& m.Arguments[0].Type != typeof(string)
&& m.Arguments.Count == 2
&& methodArgs.Length == 1
&& methodArgs[0].NodeType == ExpressionType.MemberAccess
&& TypeHelper.IsTypeAssignableFrom<IEnumerable>(m.Arguments[0].Type))
{
goto case "SqlIn";
}
string compareValue;
if (methodArgs[0].NodeType != ExpressionType.Constant)
@@ -609,6 +597,13 @@ namespace Umbraco.Core.Persistence.Querying
compareValue = methodArgs[0].ToString();
}
//special case, if it is 'Contains' and the member that Contains is being called on is not a string, then
// we should be doing an 'In' clause - but we currently do not support this
if (methodArgs[0].Type != typeof(string) && TypeHelper.IsTypeAssignableFrom<IEnumerable>(methodArgs[0].Type))
{
throw new NotSupportedException("An array Contains method is not supported");
}
//default column type
var colType = TextColumnType.NVarchar;
@@ -710,33 +705,29 @@ namespace Umbraco.Core.Persistence.Querying
// }
// return string.Format("{0}{1}", r, s);
case "SqlIn":
//case "In":
if (m.Object == null && methodArgs.Length == 1 && methodArgs[0].NodeType == ExpressionType.MemberAccess)
{
var memberAccess = VisitMemberAccess((MemberExpression) methodArgs[0]);
var member = Expression.Convert(m.Arguments[0], typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
// var member = Expression.Convert(m.Arguments[0], typeof(object));
// var lambda = Expression.Lambda<Func<object>>(member);
// var getter = lambda.Compile();
var inArgs = (IEnumerable)getter();
// var inArgs = (object[])getter();
var sIn = new StringBuilder();
foreach (var e in inArgs)
{
SqlParameters.Add(e);
// var sIn = new StringBuilder();
// foreach (var e in inArgs)
// {
// SqlParameters.Add(e);
sIn.AppendFormat("{0}{1}",
sIn.Length > 0 ? "," : "",
string.Format("@{0}", SqlParameters.Count - 1));
}
// sIn.AppendFormat("{0}{1}",
// sIn.Length > 0 ? "," : "",
// string.Format("@{0}", SqlParameters.Count - 1));
return string.Format("{0} IN ({1})", memberAccess, sIn);
}
throw new NotSupportedException("SqlIn must contain the member being accessed");
// //sIn.AppendFormat("{0}{1}",
// // sIn.Length > 0 ? "," : "",
// // GetQuotedValue(e, e.GetType()));
// }
// return string.Format("{0} {1} ({2})", r, m.Method.Name, sIn.ToString());
//case "Desc":
// return string.Format("{0} DESC", r);
//case "Alias":
@@ -1,6 +1,5 @@
using System;
using System.Linq.Expressions;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -13,19 +12,16 @@ namespace Umbraco.Core.Persistence.Querying
/// <remarks>This object is stateful and cannot be re-used to parse an expression.</remarks>
internal class ModelToSqlExpressionVisitor<T> : ExpressionVisitorBase
{
private readonly MappingResolver _mappingResolver;
private readonly BaseMapper _mapper;
public ModelToSqlExpressionVisitor(ISqlSyntaxProvider sqlSyntax, MappingResolver mappingResolver)
public ModelToSqlExpressionVisitor(ISqlSyntaxProvider sqlSyntax, BaseMapper mapper)
: base(sqlSyntax)
{
_mapper = mappingResolver.ResolveMapperByType(typeof(T));
_mappingResolver = mappingResolver;
_mapper = mapper;
}
[Obsolete("Use the overload the specifies a SqlSyntaxProvider")]
public ModelToSqlExpressionVisitor()
: this(SqlSyntaxContext.SqlSyntaxProvider, MappingResolver.Current)
: this(SqlSyntaxContext.SqlSyntaxProvider, MappingResolver.Current.ResolveMapperByType(typeof(T)))
{ }
protected override string VisitMemberAccess(MemberExpression m)
@@ -39,7 +35,7 @@ namespace Umbraco.Core.Persistence.Querying
{
var field = _mapper.Map(m.Member.Name, true);
if (field.IsNullOrWhiteSpace())
throw new InvalidOperationException(string.Format("The mapper returned an empty field for the member name: {0} for type: {1}", m.Member.Name, m.Expression.Type));
throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name);
return field;
}
//already compiled, return
@@ -53,42 +49,13 @@ namespace Umbraco.Core.Persistence.Querying
{
var field = _mapper.Map(m.Member.Name, true);
if (field.IsNullOrWhiteSpace())
throw new InvalidOperationException(string.Format("The mapper returned an empty field for the member name: {0} for type: {1}", m.Member.Name, m.Expression.Type));
throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name);
return field;
}
//already compiled, return
return string.Empty;
}
if (m.Expression != null
&& m.Expression.Type != typeof(T)
&& TypeHelper.IsTypeAssignableFrom<IUmbracoEntity>(m.Expression.Type)
&& EndsWithConstant(m) == false)
{
//if this is the case, it means we have a sub expression / nested property access, such as: x.ContentType.Alias == "Test";
//and since the sub type (x.ContentType) is not the same as x, we need to resolve a mapper for x.ContentType to get it's mapped SQL column
//don't execute if compiled
if (Visited == false)
{
var subMapper = _mappingResolver.ResolveMapperByType(m.Expression.Type);
if (subMapper == null)
throw new NullReferenceException("No mapper found for type " + m.Expression.Type);
var field = subMapper.Map(m.Member.Name, true);
if (field.IsNullOrWhiteSpace())
throw new InvalidOperationException(string.Format("The mapper returned an empty field for the member name: {0} for type: {1}", m.Member.Name, m.Expression.Type));
return field;
}
//already compiled, return
return string.Empty;
}
//TODO: When m.Expression.NodeType == ExpressionType.Constant and it's an expression like: content => aliases.Contains(content.ContentType.Alias);
// then an SQL parameter will be added for aliases as an array, however in SqlIn on the subclass it will manually add these SqlParameters anyways,
// however the query will still execute because the SQL that is written will only contain the correct indexes of SQL parameters, this would be ignored,
// I'm just unsure right now due to time constraints how to make it correct. It won't matter right now and has been working already with this bug but I've
// only just discovered what it is actually doing.
var member = Expression.Convert(m, typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
@@ -103,24 +70,5 @@ namespace Umbraco.Core.Persistence.Querying
return string.Empty;
}
/// <summary>
/// Determines if the MemberExpression ends in a Constant value
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
private bool EndsWithConstant(MemberExpression m)
{
Expression expr = m;
while (expr is MemberExpression)
{
var memberExpr = expr as MemberExpression;
expr = memberExpr.Expression;
}
var constExpr = expr as ConstantExpression;
return constExpr != null;
}
}
}
@@ -18,13 +18,14 @@ namespace Umbraco.Core.Persistence.Querying
public PocoToSqlExpressionVisitor(ISqlSyntaxProvider syntaxProvider)
: base(syntaxProvider)
{
_pd = new Database.PocoData(typeof(T));
}
[Obsolete("Use the overload the specifies a SqlSyntaxProvider")]
public PocoToSqlExpressionVisitor()
: this(SqlSyntaxContext.SqlSyntaxProvider)
{
: base(SqlSyntaxContext.SqlSyntaxProvider)
{
_pd = new Database.PocoData(typeof(T));
}
protected override string VisitMemberAccess(MemberExpression m)
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using System;
using System.Text.RegularExpressions;
namespace Umbraco.Core.Persistence.Querying
@@ -7,13 +6,8 @@ namespace Umbraco.Core.Persistence.Querying
/// <summary>
/// String extension methods used specifically to translate into SQL
/// </summary>
internal static class SqlExpressionExtensions
internal static class SqlStringExtensions
{
public static bool SqlIn<T>(this IEnumerable<T> collection, T item)
{
return collection.Contains(item);
}
public static bool SqlWildcard(this string str, string txt, TextColumnType columnType)
{
var wildcardmatch = new Regex("^" + Regex.Escape(txt).
@@ -1,31 +0,0 @@
namespace Umbraco.Core.Persistence.Repositories
{
internal enum BaseQueryType
{
/// <summary>
/// A query to return all information for a single item
/// </summary>
/// <remarks>
/// In some cases this will be the same as <see cref="FullMultiple"/>
/// </remarks>
FullSingle,
/// <summary>
/// A query to return all information for multiple items
/// </summary>
/// <remarks>
/// In some cases this will be the same as <see cref="FullSingle"/>
/// </remarks>
FullMultiple,
/// <summary>
/// A query to return the ids for items
/// </summary>
Ids,
/// <summary>
/// A query to return the count for items
/// </summary>
Count
}
}
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Xml;
@@ -54,9 +53,9 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IContent PerformGet(int id)
{
var sql = GetBaseQuery(BaseQueryType.FullSingle)
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.Where<DocumentDto>(x => x.Newest, SqlSyntax)
.Where<DocumentDto>(x => x.Newest)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
@@ -64,112 +63,70 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto == null)
return null;
var content = CreateContentFromDto(dto, sql);
var content = CreateContentFromDto(dto, dto.ContentVersionDto.VersionId, sql);
return content;
}
protected override IEnumerable<IContent> PerformGetAll(params int[] ids)
{
Func<Sql, Sql> translate = s =>
var sql = GetBaseQuery(false);
if (ids.Any())
{
if (ids.Any())
{
s.Where("umbracoNode.id in (@ids)", new { ids });
}
//we only want the newest ones with this method
s.Where<DocumentDto>(x => x.Newest, SqlSyntax);
return s;
};
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
sql.Where("umbracoNode.id in (@ids)", new { ids });
}
return ProcessQuery(translate(sqlBaseFull), new PagingSqlQuery(translate(sqlBaseIds)));
//we only want the newest ones with this method
sql.Where<DocumentDto>(x => x.Newest);
return ProcessQuery(sql);
}
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
{
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate()
.Where<DocumentDto>(x => x.Newest)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
.OrderBy<NodeDto>(x => x.SortOrder);
Func<SqlTranslator<IContent>, Sql> translate = (translator) =>
{
return translator.Translate()
.Where<DocumentDto>(x => x.Newest, SqlSyntax)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax)
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
};
var translatorFull = new SqlTranslator<IContent>(sqlBaseFull, query);
var translatorIds = new SqlTranslator<IContent>(sqlBaseIds, query);
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)));
return ProcessQuery(sql);
}
#endregion
#region Overrides of PetaPocoRepositoryBase<IContent>
/// <summary>
/// Returns the base query to return Content
/// </summary>
/// <param name="queryType"></param>
/// <returns></returns>
/// <remarks>
/// Content queries will differ depending on what needs to be returned:
/// * FullSingle: When querying for a single document, this will include the Outer join to fetch the content item's published version info
/// * FullMultiple: When querying for multiple documents, this will exclude the Outer join to fetch the content item's published version info - this info would need to be fetched separately
/// * Ids: This would essentially be the same as FullMultiple however the columns specified will only return the Ids for the documents
/// * Count: A query to return the count for documents
/// </remarks>
protected override Sql GetBaseQuery(BaseQueryType queryType)
protected override Sql GetBaseQuery(bool isCount)
{
var sql = new Sql();
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsDocument.nodeId" : "*"))
.From<DocumentDto>(SqlSyntax)
.InnerJoin<ContentVersionDto>(SqlSyntax)
.On<DocumentDto, ContentVersionDto>(SqlSyntax, left => left.VersionId, right => right.VersionId)
.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
//TODO: IF we want to enable querying on content type information this will need to be joined
//.InnerJoin<ContentTypeDto>(SqlSyntax)
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
if (queryType == BaseQueryType.FullSingle)
{
//The only reason we apply this left outer join is to be able to pull back the DocumentPublishedReadOnlyDto
//information with the entire data set, so basically this will get both the latest document and also it's published
//version if it has one. When performing a count or when retrieving Ids like in paging, this is unecessary
//and causes huge performance overhead for the SQL server, especially when sorting the result.
//We also don't include this outer join when querying for multiple entities since it is much faster to fetch this information
//in a separate query. For a single entity this is ok.
var sqlx = string.Format("LEFT OUTER JOIN {0} {1} ON ({1}.{2}={0}.{2} AND {1}.{3}=1)",
var sqlx = string.Format("LEFT OUTER JOIN {0} {1} ON ({1}.{2}={0}.{2} AND {1}.{3}=1)",
SqlSyntax.GetQuotedTableName("cmsDocument"),
SqlSyntax.GetQuotedTableName("cmsDocument2"),
SqlSyntax.GetQuotedColumnName("nodeId"),
SqlSyntax.GetQuotedColumnName("published"));
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*")
.From<DocumentDto>()
.InnerJoin<ContentVersionDto>()
.On<DocumentDto, ContentVersionDto>(left => left.VersionId, right => right.VersionId)
.InnerJoin<ContentDto>()
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
// cannot do this because PetaPoco does not know how to alias the table
//.LeftOuterJoin<DocumentPublishedReadOnlyDto>()
//.On<DocumentDto, DocumentPublishedReadOnlyDto>(left => left.NodeId, right => right.NodeId)
// so have to rely on writing our own SQL
sql.Append(sqlx /*, new { @published = true }*/);
}
sql.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
.Append(sqlx/*, new { @published = true }*/)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
return sql;
}
protected override Sql GetBaseQuery(bool isCount)
{
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
}
protected override string GetBaseWhereClause()
{
return "umbracoNode.id = @Id";
@@ -216,32 +173,20 @@ namespace Umbraco.Core.Persistence.Repositories
// not bring much safety - so this reverts to updating each record individually,
// and it may be slower in the end, but should be more resilient.
var contentTypeIdsA = contentTypeIds == null ? new int[0] : contentTypeIds.ToArray();
Func<int, Sql, Sql> translate = (bId, sql) =>
{
if (contentTypeIdsA.Length > 0)
{
sql.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
}
sql
.Where<NodeDto>(x => x.NodeId > bId && x.Trashed == false, SqlSyntax)
.Where<DocumentDto>(x => x.Published, SqlSyntax)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
return sql;
};
var baseId = 0;
var contentTypeIdsA = contentTypeIds == null ? new int[0] : contentTypeIds.ToArray();
while (true)
{
// get the next group of nodes
var sqlFull = translate(baseId, GetBaseQuery(BaseQueryType.FullMultiple));
var sqlIds = translate(baseId, GetBaseQuery(BaseQueryType.Ids));
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(sqlFull, groupSize), new PagingSqlQuery(SqlSyntax.SelectTop(sqlIds, groupSize)))
var query = GetBaseQuery(false);
if (contentTypeIdsA.Length > 0)
query = query
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
query = query
.Where<NodeDto>(x => x.NodeId > baseId && x.Trashed == false)
.Where<DocumentDto>(x => x.Published)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
.ToList();
@@ -261,70 +206,30 @@ namespace Umbraco.Core.Persistence.Repositories
Logger.Error<MediaRepository>("Could not rebuild XML for nodeId=" + xmlItem.NodeId, e);
}
}
baseId = xmlItems[xmlItems.Count - 1].NodeId;
baseId = xmlItems.Last().NodeId;
}
//now delete the items that shouldn't be there
var sqlAllIds = translate(0, GetBaseQuery(BaseQueryType.Ids));
var allContentIds = Database.Fetch<int>(sqlAllIds);
var docObjectType = Guid.Parse(Constants.ObjectTypes.Document);
var xmlIdsQuery = new Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>(SqlSyntax)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentXmlDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
if (contentTypeIdsA.Length > 0)
{
xmlIdsQuery.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>(SqlSyntax)
.On<ContentTypeDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.ContentTypeId)
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
}
xmlIdsQuery.Where<NodeDto>(dto => dto.NodeObjectType == docObjectType, SqlSyntax);
var allXmlIds = Database.Fetch<int>(xmlIdsQuery);
var toRemove = allXmlIds.Except(allContentIds).ToArray();
if (toRemove.Length > 0)
{
foreach (var idGroup in toRemove.InGroupsOf(2000))
{
Database.Execute("DELETE FROM cmsContentXml WHERE nodeId IN (@ids)", new { ids = idGroup });
}
}
}
public override IEnumerable<IContent> GetAllVersions(int id)
{
Func<Sql, Sql> translate = s =>
{
return s.Where(GetBaseWhereClause(), new {Id = id})
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
};
var sqlFull = translate(GetBaseQuery(BaseQueryType.FullMultiple));
var sqlIds = translate(GetBaseQuery(BaseQueryType.Ids));
return ProcessQuery(sqlFull, new PagingSqlQuery(sqlIds), true, includeAllVersions:true);
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
return ProcessQuery(sql, true);
}
public override IContent GetByVersion(Guid versionId)
{
var sql = GetBaseQuery(BaseQueryType.FullSingle);
//TODO: cmsContentVersion.VersionId has a Unique Index constraint applied, seems silly then to also add OrderByDescending since it would be impossible to return more than one.
var sql = GetBaseQuery(false);
sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId });
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql).FirstOrDefault();
if (dto == null)
return null;
var content = CreateContentFromDto(dto, sql);
var content = CreateContentFromDto(dto, versionId, sql);
return content;
}
@@ -333,11 +238,10 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = new Sql()
.Select("*")
.From<DocumentDto>(SqlSyntax)
.InnerJoin<ContentVersionDto>(SqlSyntax)
.On<ContentVersionDto, DocumentDto>(SqlSyntax, left => left.VersionId, right => right.VersionId)
.Where<ContentVersionDto>(x => x.VersionId == versionId, SqlSyntax)
.Where<DocumentDto>(x => x.Newest != true, SqlSyntax);
.From<DocumentDto>()
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, DocumentDto>(left => left.VersionId, right => right.VersionId)
.Where<ContentVersionDto>(x => x.VersionId == versionId)
.Where<DocumentDto>(x => x.Newest != true);
var dto = Database.Fetch<DocumentDto, ContentVersionDto>(sql).FirstOrDefault();
if (dto == null) return;
@@ -355,8 +259,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = new Sql()
.Select("*")
.From<DocumentDto>()
.InnerJoin<ContentVersionDto>()
.On<ContentVersionDto, DocumentDto>(left => left.VersionId, right => right.VersionId)
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, DocumentDto>(left => left.VersionId, right => right.VersionId)
.Where<ContentVersionDto>(x => x.NodeId == id)
.Where<ContentVersionDto>(x => x.VersionDate < versionDate)
.Where<DocumentDto>(x => x.Newest != true);
@@ -596,7 +499,6 @@ namespace Umbraco.Core.Persistence.Repositories
//if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished))
if (entity.ShouldClearPublishedFlagForPreviousVersions(publishedState, shouldCreateNewVersion))
{
//TODO: This perf can be improved, it could easily be UPDATE WHERE.... (one SQL call instead of many)
var publishedDocs = Database.Fetch<DocumentDto>("WHERE nodeId = @Id AND published = @IsPublished", new { Id = entity.Id, IsPublished = true });
foreach (var doc in publishedDocs)
{
@@ -610,7 +512,6 @@ namespace Umbraco.Core.Persistence.Repositories
}
//Look up (newest) entries by id in cmsDocument table to set newest = false
//TODO: This perf can be improved, it could easily be UPDATE WHERE.... (one SQL call instead of many)
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId = @Id AND newest = @IsNewest", new { Id = entity.Id, IsNewest = true });
foreach (var documentDto in documentDtos)
{
@@ -715,27 +616,21 @@ namespace Umbraco.Core.Persistence.Repositories
public IEnumerable<IContent> GetByPublishedVersion(IQuery<IContent> query)
{
Func<SqlTranslator<IContent>, Sql> translate = t =>
{
return t.Translate()
.Where<DocumentDto>(x => x.Published, SqlSyntax)
.OrderBy<NodeDto>(x => x.Level, SqlSyntax)
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
};
// we WANT to return contents in top-down order, ie parents should come before children
// ideal would be pure xml "document order" which can be achieved with:
// ORDER BY substring(path, 1, len(path) - charindex(',', reverse(path))), sortOrder
// but that's probably an overkill - sorting by level,sortOrder should be enough
var sqlFull = GetBaseQuery(BaseQueryType.FullMultiple);
var translatorFull = new SqlTranslator<IContent>(sqlFull, query);
var sqlIds = GetBaseQuery(BaseQueryType.Ids);
var translatorIds = new SqlTranslator<IContent>(sqlIds, query);
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate()
.Where<DocumentDto>(x => x.Published)
.OrderBy<NodeDto>(x => x.Level, SqlSyntax)
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)), true);
return ProcessQuery(sql, true);
}
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
@@ -767,14 +662,10 @@ namespace Umbraco.Core.Persistence.Repositories
parent.Attributes.Append(pIdAtt);
xmlDoc.AppendChild(parent);
//Ensure that only nodes that have published versions are selected
var sql = string.Format(@"select umbracoNode.id, umbracoNode.parentID, umbracoNode.sortOrder, cmsContentXml.{0}, umbracoNode.{1} from umbracoNode
const string sql = @"select umbracoNode.id, umbracoNode.parentID, umbracoNode.sortOrder, cmsContentXml.xml, umbracoNode.level from umbracoNode
inner join cmsContentXml on cmsContentXml.nodeId = umbracoNode.id and umbracoNode.nodeObjectType = @type
where umbracoNode.id in (select cmsDocument.nodeId from cmsDocument where cmsDocument.published = 1)
order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
SqlSyntax.GetQuotedColumnName("xml"),
SqlSyntax.GetQuotedColumnName("level"),
SqlSyntax.GetQuotedColumnName("level"));
order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
XmlElement last = null;
@@ -825,8 +716,13 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
public void ClearPublished(IContent content)
{
var sql = "UPDATE cmsDocument SET published=0 WHERE nodeId=@id AND published=1";
Database.Execute(sql, new {id = content.Id});
// race cond!
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId=@id AND published=@published", new { id = content.Id, published = true });
foreach (var documentDto in documentDtos)
{
documentDto.Published = false;
Database.Update(documentDto);
}
}
/// <summary>
@@ -906,9 +802,9 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
Func<Tuple<string, object[]>> filterCallback = () => new Tuple<string, object[]>(filterSql.SQL, filterSql.Arguments);
return GetPagedResultsByQuery<DocumentDto>(query, pageIndex, pageSize, out totalRecords,
return GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out totalRecords,
new Tuple<string, string>("cmsDocument", "nodeId"),
(sqlFull, pagingSqlQuery) => ProcessQuery(sqlFull, pagingSqlQuery), orderBy, orderDirection, orderBySystemField,
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
filterCallback);
}
@@ -939,110 +835,50 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
return base.GetDatabaseFieldNameForOrderBy(orderBy);
}
/// <summary>
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The FullMultiple SQL without the outer join to return all data required to create an IContent excluding it's published state data which this will query separately
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL without the outer join to just return all document ids - used to process the properties for the content item
/// </param>
/// <param name="withCache"></param>
/// <param name="includeAllVersions">
/// Generally when querying for content we only want to return the most recent version of the content item, however in some cases like when
/// we want to return all versions of a content item, we can't simply return the latest
/// </param>
/// <returns></returns>
private IEnumerable<IContent> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false, bool includeAllVersions = false)
private IEnumerable<IContent> ProcessQuery(Sql sql, bool withCache = false)
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(sqlFull);
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql);
if (dtos.Count == 0) return Enumerable.Empty<IContent>();
//Go and get all of the published version data separately for this data, this is because when we are querying
//for multiple content items we don't include the outer join to fetch this data in the same query because
//it is insanely slow. Instead we just fetch the published version data separately in one query.
//we need to parse the original SQL statement and reduce the columns to just cmsDocument.nodeId so that we can use
// the statement to go get the published data for all of the items by using an inner join
var parsedOriginalSql = "SELECT cmsDocument.nodeId " + sqlFull.SQL.Substring(sqlFull.SQL.IndexOf("FROM", StringComparison.Ordinal));
//now remove everything from an Orderby clause and beyond
if (parsedOriginalSql.InvariantContains("ORDER BY "))
{
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
}
//order by update date DESC, if there is corrupted published flags we only want the latest!
var publishedSql = new Sql(@"SELECT cmsDocument.nodeId, cmsDocument.published, cmsDocument.versionId, cmsDocument.newest
FROM cmsDocument INNER JOIN cmsContentVersion ON cmsContentVersion.VersionId = cmsDocument.versionId
WHERE cmsDocument.published = 1 AND cmsDocument.nodeId IN
(" + parsedOriginalSql + @")
ORDER BY cmsContentVersion.id DESC
", sqlFull.Arguments);
//go and get the published version data, we do a Query here and not a Fetch so we are
//not allocating a whole list to memory just to allocate another list in memory since
//we are assigning this data to a keyed collection for fast lookup below
var publishedData = Database.Query<DocumentPublishedReadOnlyDto>(publishedSql);
var publishedDataCollection = new DocumentPublishedReadOnlyDtoCollection();
foreach (var publishedDto in publishedData)
{
//double check that there's no corrupt db data, there should only be a single published item
if (publishedDataCollection.Contains(publishedDto.NodeId) == false)
publishedDataCollection.Add(publishedDto);
}
//This is a tuple list identifying if the content item came from the cache or not
var content = new List<Tuple<IContent, bool>>();
var defs = new DocumentDefinitionCollection(includeAllVersions);
var content = new IContent[dtos.Count];
var defs = new List<DocumentDefinition>();
var templateIds = new List<int>();
//track the looked up content types, even though the content types are cached
// they still need to be deep cloned out of the cache and we don't want to add
// the overhead of deep cloning them on every item in this loop
var contentTypes = new Dictionary<int, IContentType>();
foreach (var dto in dtos)
for (var i = 0; i < dtos.Count; i++)
{
DocumentPublishedReadOnlyDto publishedDto;
publishedDataCollection.TryGetValue(dto.NodeId, out publishedDto);
var dto = dtos[i];
// if the cache contains the published version, use it
if (withCache)
{
var cached = RuntimeCache.GetCacheItem<IContent>(GetCacheIdKey<IContent>(dto.NodeId));
//only use this cached version if the dto returned is also the publish version, they must match and be teh same version
if (cached != null && cached.Version == dto.VersionId && cached.Published && dto.Published)
//only use this cached version if the dto returned is also the publish version, they must match
if (cached != null && cached.Published && dto.Published)
{
content.Add(new Tuple<IContent, bool>(cached, true));
content[i] = cached;
continue;
}
}
// else, need to fetch from the database
// content type repository is full-cache so OK to get each one independently
var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
content[i] = factory.BuildEntity(dto);
IContentType contentType;
if (contentTypes.ContainsKey(dto.ContentVersionDto.ContentDto.ContentTypeId))
{
contentType = contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId];
}
else
{
contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId] = contentType;
}
// need template
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
templateIds.Add(dto.TemplateId.Value);
// track the definition and if it's successfully added or updated then processed
if (defs.AddOrUpdate(new DocumentDefinition(dto, contentType)))
{
// assign template
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
templateIds.Add(dto.TemplateId.Value);
content.Add(new Tuple<IContent, bool>(ContentFactory.BuildEntity(dto, contentType, publishedDto), false));
}
// need properties
defs.Add(new DocumentDefinition(
dto.NodeId,
dto.VersionId,
dto.ContentVersionDto.VersionDate,
dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
contentType
));
}
// load all required templates in 1 query
@@ -1050,44 +886,45 @@ ORDER BY cmsContentVersion.id DESC
.ToDictionary(x => x.Id, x => x);
// load all properties for all documents from database in 1 query
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
var propertyData = GetPropertyCollection(sql, defs);
// assign template and property data
foreach (var contentItem in content)
// assign
var dtoIndex = 0;
foreach (var def in defs)
{
var cc = contentItem.Item1;
var fromCache = contentItem.Item2;
//if this has come from cache, we do not need to build up it's structure
if (fromCache) continue;
var def = defs[includeAllVersions ? (ValueType)cc.Version : cc.Id];
// move to corresponding item (which has to exist)
while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++;
// complete the item
var cc = content[dtoIndex];
var dto = dtos[dtoIndex];
ITemplate template = null;
if (def.DocumentDto.TemplateId.HasValue)
templates.TryGetValue(def.DocumentDto.TemplateId.Value, out template); // else null
if (dto.TemplateId.HasValue)
templates.TryGetValue(dto.TemplateId.Value, out template); // else null
cc.Template = template;
cc.Properties = propertyData[cc.Version];
cc.Properties = propertyData[cc.Id];
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
cc.ResetDirtyProperties(false);
((Entity) cc).ResetDirtyProperties(false);
}
return content.Select(x => x.Item1).ToArray();
return content;
}
/// <summary>
/// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion.
/// </summary>
/// <param name="dto"></param>
/// <param name="versionId"></param>
/// <param name="docSql"></param>
/// <returns></returns>
private IContent CreateContentFromDto(DocumentDto dto, Sql docSql)
private IContent CreateContentFromDto(DocumentDto dto, Guid versionId, Sql docSql)
{
var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
var content = ContentFactory.BuildEntity(dto, contentType);
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
var content = factory.BuildEntity(dto);
//Check if template id is set on DocumentDto, and get ITemplate if it is.
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
@@ -1095,11 +932,11 @@ ORDER BY cmsContentVersion.id DESC
content.Template = _templateRepository.Get(dto.TemplateId.Value);
}
var docDef = new DocumentDefinition(dto, contentType);
var docDef = new DocumentDefinition(dto.NodeId, versionId, content.UpdateDate, content.CreateDate, contentType);
var properties = GetPropertyCollection(docSql, new[] { docDef });
content.Properties = properties[dto.VersionId];
content.Properties = properties[dto.NodeId];
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -1138,7 +975,7 @@ ORDER BY cmsContentVersion.id DESC
return currentName;
}
/// <summary>
/// Dispose disposable properties
/// </summary>
@@ -1153,26 +990,5 @@ ORDER BY cmsContentVersion.id DESC
_contentPreviewRepository.Dispose();
_contentXmlRepository.Dispose();
}
/// <summary>
/// A keyed collection for fast lookup when retrieving a separate list of published version data
/// </summary>
private class DocumentPublishedReadOnlyDtoCollection : KeyedCollection<int, DocumentPublishedReadOnlyDto>
{
protected override int GetKeyForItem(DocumentPublishedReadOnlyDto item)
{
return item.NodeId;
}
public bool TryGetValue(int key, out DocumentPublishedReadOnlyDto val)
{
if (Dictionary == null)
{
val = null;
return false;
}
return Dictionary.TryGetValue(key, out val);
}
}
}
}
@@ -2,13 +2,19 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
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;
using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Repositories
{
@@ -112,7 +118,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (objectTypes.Any())
{
sql = sql.Where("umbracoNode.nodeObjectType IN (@objectTypes)", new {objectTypes = objectTypes});
sql = sql.Where("umbracoNode.nodeObjectType IN (@objectTypes)", objectTypes);
}
return Database.Fetch<string>(sql);
@@ -62,27 +62,17 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<EntityContainer> PerformGetAll(params int[] ids)
{
if (ids.Any())
{
//we need to batch these in groups of 2000 so we don't exceed the max 2100 limit
return ids.InGroupsOf(2000).SelectMany(@group =>
{
var sql = GetBaseQuery(false)
.Where("nodeObjectType=@umbracoObjectTypeId", new {umbracoObjectTypeId = NodeObjectTypeId})
.Where(string.Format("{0} IN (@ids)", SqlSyntax.GetQuotedColumnName("id")), new {ids = @group});
sql.OrderBy<NodeDto>(x => x.Level, SqlSyntax);
return Database.Fetch<NodeDto>(sql).Select(CreateEntity);
});
}
else
//we need to batch these in groups of 2000 so we don't exceed the max 2100 limit
return ids.InGroupsOf(2000).SelectMany(@group =>
{
var sql = GetBaseQuery(false)
.Where("nodeObjectType=@umbracoObjectTypeId", new {umbracoObjectTypeId = NodeObjectTypeId});
.Where("nodeObjectType=@umbracoObjectTypeId", new { umbracoObjectTypeId = NodeObjectTypeId })
.Where(string.Format("{0} IN (@ids)", SqlSyntax.GetQuotedColumnName("id")), new { ids = @group });
sql.OrderBy<NodeDto>(x => x.Level, SqlSyntax);
return Database.Fetch<NodeDto>(sql).Select(CreateEntity);
}
});
}
protected override IEnumerable<EntityContainer> PerformGetByQuery(IQuery<EntityContainer> query)
@@ -1,13 +1,18 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using Umbraco.Core.Models;
using Umbraco.Core;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Persistence.Repositories
{
@@ -63,12 +68,11 @@ namespace Umbraco.Core.Persistence.Repositories
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetFullSqlForEntityType(key, isContent, isMedia, objectTypeId);
var factory = new UmbracoEntityFactory();
if (isMedia)
{
//for now treat media differently and include all property data too
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
@@ -76,16 +80,14 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
//query = read forward data reader, do not load everything into mem
var dtos = _work.Database.Query<dynamic>(sql);
var collection = new EntityDefinitionCollection();
foreach (var dto in dtos)
{
collection.AddOrUpdate(new EntityDefinition(factory, dto, isContent, false));
}
var found = collection.FirstOrDefault();
return found != null ? found.BuildFromDynamic() : null;
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
return entity;
}
@@ -110,29 +112,29 @@ namespace Umbraco.Core.Persistence.Repositories
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetFullSqlForEntityType(id, isContent, isMedia, objectTypeId);
var factory = new UmbracoEntityFactory();
if (isMedia)
{
//for now treat media differently and include all property data too
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
return entities.FirstOrDefault();
}
else
{
//query = read forward data reader, do not load everything into mem
var dtos = _work.Database.Query<dynamic>(sql);
var collection = new EntityDefinitionCollection();
foreach (var dto in dtos)
{
collection.AddOrUpdate(new EntityDefinition(factory, dto, isContent, false));
}
var found = collection.FirstOrDefault();
return found != null ? found.BuildFromDynamic() : null;
{
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
return entity;
}
}
public virtual IEnumerable<IUmbracoEntity> GetAll(Guid objectTypeId, params int[] ids)
@@ -169,21 +171,22 @@ namespace Umbraco.Core.Persistence.Repositories
if (isMedia)
{
//for now treat media differently and include all property data too
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
return entities;
foreach (var entity in entities)
{
yield return entity;
}
}
else
{
//query = read forward data reader, do not load everything into mem
var dtos = _work.Database.Query<dynamic>(sql);
var collection = new EntityDefinitionCollection();
foreach (var dto in dtos)
var dtos = _work.Database.Fetch<dynamic>(sql);
foreach (var entity in dtos.Select(dto => factory.BuildEntityFromDynamic(dto)))
{
collection.AddOrUpdate(new EntityDefinition(factory, dto, isContent, false));
yield return entity;
}
return collection.Select(x => x.BuildFromDynamic()).ToList();
}
}
@@ -228,7 +231,8 @@ namespace Umbraco.Core.Persistence.Repositories
}
});
//for now treat media differently and include all property data too
//treat media differently for now
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, mediaSql);
return entities;
@@ -237,15 +241,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
//use dynamic so that we can get ALL properties from the SQL so we can chuck that data into our AdditionalData
var finalSql = entitySql.Append(GetGroupBy(isContent, false));
//query = read forward data reader, do not load everything into mem
var dtos = _work.Database.Query<dynamic>(finalSql);
var collection = new EntityDefinitionCollection();
foreach (var dto in dtos)
{
collection.AddOrUpdate(new EntityDefinition(factory, dto, isContent, false));
}
return collection.Select(x => x.BuildFromDynamic()).ToList();
var dtos = _work.Database.Fetch<dynamic>(finalSql);
return dtos.Select(factory.BuildEntityFromDynamic).Cast<IUmbracoEntity>().ToList();
}
}
@@ -318,31 +315,25 @@ namespace Umbraco.Core.Persistence.Repositories
protected virtual Sql GetBase(bool isContent, bool isMedia, Action<Sql> customFilter)
{
var columns = new List<object>
{
"umbracoNode.id",
"umbracoNode.trashed",
"umbracoNode.parentID",
"umbracoNode.nodeUser",
"umbracoNode.level",
"umbracoNode.path",
"umbracoNode.sortOrder",
"umbracoNode.uniqueID",
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate",
"COUNT(parent.parentID) as children"
};
{
"umbracoNode.id",
"umbracoNode.trashed",
"umbracoNode.parentID",
"umbracoNode.nodeUser",
"umbracoNode.level",
"umbracoNode.path",
"umbracoNode.sortOrder",
"umbracoNode.uniqueID",
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate",
"COUNT(parent.parentID) as children"
};
if (isContent || isMedia)
{
if (isContent)
{
//only content has/needs this info
columns.Add("published.versionId as publishedVersion");
columns.Add("document.versionId as newestVersion");
columns.Add("contentversion.id as versionId");
}
columns.Add("published.versionId as publishedVersion");
columns.Add("latest.versionId as newestVersion");
columns.Add("contenttype.alias");
columns.Add("contenttype.icon");
columns.Add("contenttype.thumbnail");
@@ -354,22 +345,17 @@ namespace Umbraco.Core.Persistence.Repositories
var entitySql = new Sql()
.Select(columns.ToArray())
.From("umbracoNode umbracoNode");
if (isContent || isMedia)
{
entitySql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id");
if (isContent)
{
//only content has/needs this info
entitySql
.InnerJoin("cmsDocument document").On("document.nodeId = umbracoNode.id")
.InnerJoin("cmsContentVersion contentversion").On("contentversion.VersionId = document.versionId")
.LeftJoin("(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1) as published")
.On("umbracoNode.id = published.nodeId");
}
entitySql.LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType");
entitySql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id")
.LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType")
.LeftJoin(
"(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1 GROUP BY nodeId, versionId) as published")
.On("umbracoNode.id = published.nodeId")
.LeftJoin(
"(SELECT nodeId, versionId FROM cmsDocument WHERE newest = 1 GROUP BY nodeId, versionId) as latest")
.On("umbracoNode.id = latest.nodeId");
}
entitySql.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
@@ -386,42 +372,22 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = baseQuery(isContent, isMedia, filter)
.Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = nodeObjectType });
if (isContent)
{
sql.Where("document.newest = 1");
}
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, int id)
{
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @Id", new { Id = id });
if (isContent)
{
sql.Where("document.newest = 1");
}
sql.Append(GetGroupBy(isContent, isMedia));
.Where("umbracoNode.id = @Id", new { Id = id })
.Append(GetGroupBy(isContent, isMedia));
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Guid key)
{
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @UniqueID", new {UniqueID = key});
if (isContent)
{
sql.Where("document.newest = 1");
}
sql.Append(GetGroupBy(isContent, isMedia));
.Where("umbracoNode.uniqueID = @UniqueID", new { UniqueID = key })
.Append(GetGroupBy(isContent, isMedia));
return sql;
}
@@ -430,12 +396,6 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @Id AND umbracoNode.nodeObjectType = @NodeObjectType",
new {Id = id, NodeObjectType = nodeObjectType});
if (isContent)
{
sql.Where("document.newest = 1");
}
return sql;
}
@@ -444,46 +404,36 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @UniqueID AND umbracoNode.nodeObjectType = @NodeObjectType",
new { UniqueID = key, NodeObjectType = nodeObjectType });
if (isContent)
{
sql.Where("document.newest = 1");
}
return sql;
}
protected virtual Sql GetGroupBy(bool isContent, bool isMedia, bool includeSort = true)
{
var columns = new List<object>
{
"umbracoNode.id",
"umbracoNode.trashed",
"umbracoNode.parentID",
"umbracoNode.nodeUser",
"umbracoNode.level",
"umbracoNode.path",
"umbracoNode.sortOrder",
"umbracoNode.uniqueID",
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate"
};
{
"umbracoNode.id",
"umbracoNode.trashed",
"umbracoNode.parentID",
"umbracoNode.nodeUser",
"umbracoNode.level",
"umbracoNode.path",
"umbracoNode.sortOrder",
"umbracoNode.uniqueID",
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate"
};
if (isContent || isMedia)
{
if (isContent)
{
columns.Add("published.versionId");
columns.Add("document.versionId");
columns.Add("contentversion.id");
}
columns.Add("published.versionId");
columns.Add("latest.versionId");
columns.Add("contenttype.alias");
columns.Add("contenttype.icon");
columns.Add("contenttype.thumbnail");
columns.Add("contenttype.isContainer");
columns.Add("contenttype.isContainer");
}
var sql = new Sql()
.GroupBy(columns.ToArray());
@@ -494,7 +444,7 @@ namespace Umbraco.Core.Persistence.Repositories
return sql;
}
#endregion
/// <summary>
@@ -508,7 +458,33 @@ namespace Umbraco.Core.Persistence.Repositories
UnitOfWork.DisposeIfDisposable();
}
#region private classes
#region umbracoNode POCO - Extends NodeDto
[TableName("umbracoNode")]
[PrimaryKey("id")]
[ExplicitColumns]
internal class UmbracoEntityDto : NodeDto
{
[Column("children")]
public int Children { get; set; }
[Column("publishedVersion")]
public Guid PublishedVersion { get; set; }
[Column("newestVersion")]
public Guid NewestVersion { get; set; }
[Column("alias")]
public string Alias { get; set; }
[Column("icon")]
public string Icon { get; set; }
[Column("thumbnail")]
public string Thumbnail { get; set; }
[ResultColumn]
public List<UmbracoPropertyDto> UmbracoPropertyDtos { get; set; }
}
[ExplicitColumns]
internal class UmbracoPropertyDto
@@ -592,99 +568,6 @@ namespace Umbraco.Core.Persistence.Repositories
return prev;
}
}
private class EntityDefinitionCollection : KeyedCollection<int, EntityDefinition>
{
protected override int GetKeyForItem(EntityDefinition item)
{
return item.Id;
}
/// <summary>
/// if this key already exists if it does then we need to check
/// if the existing item is 'older' than the new item and if that is the case we'll replace the older one
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool AddOrUpdate(EntityDefinition item)
{
if (Dictionary == null)
{
base.Add(item);
return true;
}
var key = GetKeyForItem(item);
EntityDefinition found;
if (TryGetValue(key, out found))
{
//it already exists and it's older so we need to replace it
if (item.VersionId > found.VersionId)
{
var currIndex = Items.IndexOf(found);
if (currIndex == -1)
throw new IndexOutOfRangeException("Could not find the item in the list: " + found.Id);
//replace the current one with the newer one
SetItem(currIndex, item);
return true;
}
//could not add or update
return false;
}
base.Add(item);
return true;
}
private bool TryGetValue(int key, out EntityDefinition val)
{
if (Dictionary == null)
{
val = null;
return false;
}
return Dictionary.TryGetValue(key, out val);
}
}
private class EntityDefinition
{
private readonly UmbracoEntityFactory _factory;
private readonly dynamic _entity;
private readonly bool _isContent;
private readonly bool _isMedia;
public EntityDefinition(UmbracoEntityFactory factory, dynamic entity, bool isContent, bool isMedia)
{
_factory = factory;
_entity = entity;
_isContent = isContent;
_isMedia = isMedia;
}
public IUmbracoEntity BuildFromDynamic()
{
return _factory.BuildEntityFromDynamic(_entity);
}
public int Id
{
get { return _entity.id; }
}
public int VersionId
{
get
{
if (_isContent || _isMedia)
{
return _entity.versionId;
}
return _entity.id;
}
}
}
#endregion
}
}
@@ -96,6 +96,5 @@ namespace Umbraco.Core.Persistence.Repositories
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IContent> GetPagedResultsByQuery(IQuery<IContent> query, long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, bool orderBySystemField, IQuery<IContent> filter = null);
}
}
@@ -38,6 +38,15 @@ namespace Umbraco.Core.Persistence.Repositories
/// <returns>An Enumerable list of <see cref="IMedia"/> objects</returns>
IEnumerable<IMedia> GetPagedResultsByQuery(IQuery<IMedia> query, long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, bool orderBySystemField, string filter = "");
/// <summary>
/// Gets paged media descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of media items</returns>
IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords);
}
}
@@ -73,6 +73,6 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="content"></param>
/// <param name="xml"></param>
void AddOrUpdatePreviewXml(IMember content, Func<IMember, XElement> xml);
}
}
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="serializer">The serializer to convert TEntity to Xml</param>
/// <param name="groupSize">Structures will be rebuilt in chunks of this size</param>
/// <param name="contentTypeIds"></param>
void RebuildXmlStructures(Func<TEntity, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null);
void RebuildXmlStructures(Func<TEntity, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null);
/// <summary>
/// Get the total count of entities
@@ -67,16 +67,5 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="id">Id of the <see cref="TEntity"/> object to delete versions from</param>
/// <param name="versionDate">Latest version date</param>
void DeleteVersions(int id, DateTime versionDate);
/// <summary>
/// Gets paged content descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="orderBy"></param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, string[] orderBy, out long totalRecords);
}
}
@@ -55,7 +55,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto == null)
return null;
var content = CreateMediaFromDto(dto, sql);
var content = CreateMediaFromDto(dto, dto.VersionId, sql);
return content;
}
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
}
return ProcessQuery(sql, new PagingSqlQuery(sql));
return ProcessQuery(sql);
}
protected override IEnumerable<IMedia> PerformGetByQuery(IQuery<IMedia> query)
@@ -76,34 +76,26 @@ namespace Umbraco.Core.Persistence.Repositories
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IMedia>(sqlClause, query);
var sql = translator.Translate()
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(sql, new PagingSqlQuery(sql));
return ProcessQuery(sql);
}
#endregion
#region Overrides of PetaPocoRepositoryBase<int,IMedia>
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
var sql = new Sql();
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsContentVersion.contentId" : "*"))
.From<ContentVersionDto>(SqlSyntax)
.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId, SqlSyntax)
//TODO: IF we want to enable querying on content type information this will need to be joined
//.InnerJoin<ContentTypeDto>(SqlSyntax)
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
return sql;
}
protected override Sql GetBaseQuery(bool isCount)
{
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*")
.From<ContentVersionDto>()
.InnerJoin<ContentDto>()
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
return sql;
}
protected override string GetBaseWhereClause()
@@ -146,106 +138,93 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
return ProcessQuery(sql, new PagingSqlQuery(sql), true);
return ProcessQuery(sql, true);
}
/// <summary>
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The full SQL to select all media data
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL to just return all media ids - used to process the properties for the media item
/// </param>
/// <param name="withCache"></param>
/// <returns></returns>
private IEnumerable<IMedia> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
private IEnumerable<IMedia> ProcessQuery(Sql sql, bool withCache = false)
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sqlFull);
//This is a tuple list identifying if the content item came from the cache or not
var content = new List<Tuple<IMedia, bool>>();
var defs = new DocumentDefinitionCollection();
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql);
var content = new IMedia[dtos.Count];
var defs = new List<DocumentDefinition>();
//track the looked up content types, even though the content types are cached
// they still need to be deep cloned out of the cache and we don't want to add
// the overhead of deep cloning them on every item in this loop
var contentTypes = new Dictionary<int, IMediaType>();
foreach (var dto in dtos)
for (var i = 0; i < dtos.Count; i++)
{
var dto = dtos[i];
// if the cache contains the item, use it
if (withCache)
{
var cached = RuntimeCache.GetCacheItem<IMedia>(GetCacheIdKey<IMedia>(dto.NodeId));
//only use this cached version if the dto returned is the same version - this is just a safety check, media doesn't
//store different versions, but just in case someone corrupts some data we'll double check to be sure.
if (cached != null && cached.Version == dto.VersionId)
if (cached != null)
{
content.Add(new Tuple<IMedia, bool>(cached, true));
content[i] = cached;
continue;
}
}
// else, need to fetch from the database
// content type repository is full-cache so OK to get each one independently
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId);
content[i] = factory.BuildEntity(dto);
IMediaType contentType;
if (contentTypes.ContainsKey(dto.ContentDto.ContentTypeId))
{
contentType = contentTypes[dto.ContentDto.ContentTypeId];
}
else
{
contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
contentTypes[dto.ContentDto.ContentTypeId] = contentType;
}
// track the definition and if it's successfully added or updated then processed
if (defs.AddOrUpdate(new DocumentDefinition(dto, contentType)))
{
content.Add(new Tuple<IMedia, bool>(MediaFactory.BuildEntity(dto, contentType), false));
}
// need properties
defs.Add(new DocumentDefinition(
dto.NodeId,
dto.VersionId,
dto.VersionDate,
dto.ContentDto.NodeDto.CreateDate,
contentType
));
}
// load all properties for all documents from database in 1 query
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
var propertyData = GetPropertyCollection(sql, defs);
// assign property data
foreach (var contentItem in content)
// assign
var dtoIndex = 0;
foreach (var def in defs)
{
var cc = contentItem.Item1;
var fromCache = contentItem.Item2;
// move to corresponding item (which has to exist)
while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++;
//if this has come from cache, we do not need to build up it's structure
if (fromCache) continue;
cc.Properties = propertyData[cc.Version];
// complete the item
var cc = content[dtoIndex];
cc.Properties = propertyData[cc.Id];
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
cc.ResetDirtyProperties(false);
((Entity) cc).ResetDirtyProperties(false);
}
return content.Select(x => x.Item1).ToArray();
return content;
}
public override IMedia GetByVersion(Guid versionId)
{
var sql = GetBaseQuery(false);
sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId });
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
var dto = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql).FirstOrDefault();
if (dto == null)
return null;
var content = CreateMediaFromDto(dto, sql);
var mediaType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
return content;
var factory = new MediaFactory(mediaType, NodeObjectTypeId, dto.NodeId);
var media = factory.BuildEntity(dto);
var properties = GetPropertyCollection(sql, new[] { new DocumentDefinition(dto.NodeId, dto.VersionId, media.UpdateDate, media.CreateDate, mediaType) });
media.Properties = properties[dto.NodeId];
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
((Entity)media).ResetDirtyProperties(false);
return media;
}
public void RebuildXmlStructures(Func<IMedia, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
@@ -263,15 +242,12 @@ namespace Umbraco.Core.Persistence.Repositories
// get the next group of nodes
var query = GetBaseQuery(false);
if (contentTypeIdsA.Length > 0)
{
query = query.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
}
query = query
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
query = query
.Where<NodeDto>(x => x.NodeId > baseId, SqlSyntax)
.Where<NodeDto>(x => x.Trashed == false, SqlSyntax)
.Where<NodeDto>(x => x.NodeId > baseId)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
var sql = SqlSyntax.SelectTop(query, groupSize);
var xmlItems = ProcessQuery(sql, new PagingSqlQuery(sql))
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
.ToList();
@@ -292,38 +268,7 @@ namespace Umbraco.Core.Persistence.Repositories
Logger.Error<MediaRepository>("Could not rebuild XML for nodeId=" + xmlItem.NodeId, e);
}
}
baseId = xmlItems[xmlItems.Count - 1].NodeId;
}
//now delete the items that shouldn't be there
var allMediaIds = Database.Fetch<int>(GetBaseQuery(BaseQueryType.Ids).Where<NodeDto>(x => x.Trashed == false, SqlSyntax));
var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
var xmlIdsQuery = new Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>(SqlSyntax)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentXmlDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
if (contentTypeIdsA.Length > 0)
{
xmlIdsQuery.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>(SqlSyntax)
.On<ContentTypeDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.ContentTypeId)
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
}
xmlIdsQuery.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType, SqlSyntax);
var allXmlIds = Database.Fetch<int>(xmlIdsQuery);
var toRemove = allXmlIds.Except(allMediaIds).ToArray();
if (toRemove.Length > 0)
{
foreach (var idGroup in toRemove.InGroupsOf(2000))
{
Database.Execute("DELETE FROM cmsContentXml WHERE nodeId IN (@ids)", new { ids = idGroup });
}
baseId = xmlItems.Last().NodeId;
}
}
@@ -550,30 +495,56 @@ namespace Umbraco.Core.Persistence.Repositories
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
}
return GetPagedResultsByQuery<ContentVersionDto>(query, pageIndex, pageSize, out totalRecords,
return GetPagedResultsByQuery<ContentVersionDto, Models.Media>(query, pageIndex, pageSize, out totalRecords,
new Tuple<string, string>("cmsContentVersion", "contentId"),
(sqlFull, pagingSqlQuery) => ProcessQuery(sqlFull, pagingSqlQuery), orderBy, orderDirection, orderBySystemField,
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
filterCallback);
}
/// <summary>
/// Gets paged media descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of media items</returns>
public IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords)
{
Sql query;
if (path == "-1")
{
query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE nodeObjectType = @0)", Guid.Parse(Constants.ObjectTypes.Media)).OrderBy("nodeId");
}
else
{
query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE path LIKE @0)", path.EnsureEndsWith(",%")).OrderBy("nodeId");
}
var pagedResult = Database.Page<ContentXmlDto>(pageIndex+1, pageSize, query);
totalRecords = pagedResult.TotalItems;
return pagedResult.Items.Select(dto => XElement.Parse(dto.Xml));
}
/// <summary>
/// Private method to create a media object from a ContentDto
/// </summary>
/// <param name="dto"></param>
/// <param name="d"></param>
/// <param name="versionId"></param>
/// <param name="docSql"></param>
/// <returns></returns>
private IMedia CreateMediaFromDto(ContentVersionDto dto, Sql docSql)
private IMedia CreateMediaFromDto(ContentVersionDto dto, Guid versionId, Sql docSql)
{
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
var media = MediaFactory.BuildEntity(dto, contentType);
var docDef = new DocumentDefinition(dto, contentType);
var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId);
var media = factory.BuildEntity(dto);
var properties = GetPropertyCollection(new PagingSqlQuery(docSql), new[] { docDef });
var docDef = new DocumentDefinition(dto.NodeId, versionId, media.UpdateDate, media.CreateDate, contentType);
media.Properties = properties[dto.VersionId];
var properties = GetPropertyCollection(docSql, new[] { docDef });
media.Properties = properties[dto.NodeId];
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -54,7 +54,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto == null)
return null;
var content = CreateMemberFromDto(dto, sql);
var content = CreateMemberFromDto(dto, dto.ContentVersionDto.VersionId, sql);
return content;
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
}
return ProcessQuery(sql, new PagingSqlQuery(sql));
return ProcessQuery(sql);
}
@@ -90,7 +90,7 @@ namespace Umbraco.Core.Persistence.Repositories
baseQuery.Append(new Sql("WHERE umbracoNode.id IN (" + sql.SQL + ")", sql.Arguments))
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(baseQuery, new PagingSqlQuery(baseQuery));
return ProcessQuery(baseQuery);
}
else
{
@@ -98,7 +98,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = translator.Translate()
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(sql, new PagingSqlQuery(sql));
return ProcessQuery(sql);
}
}
@@ -107,29 +107,24 @@ namespace Umbraco.Core.Persistence.Repositories
#region Overrides of PetaPocoRepositoryBase<int,IMembershipUser>
protected override Sql GetBaseQuery(BaseQueryType queryType)
protected override Sql GetBaseQuery(bool isCount)
{
var sql = new Sql();
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsMember.nodeId" : "*"))
.From<MemberDto>(SqlSyntax)
.InnerJoin<ContentVersionDto>(SqlSyntax)
.On<ContentVersionDto, MemberDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
sql.Select(isCount ? "COUNT(*)" : "*")
.From<MemberDto>()
.InnerJoin<ContentVersionDto>()
.On<ContentVersionDto, MemberDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentDto>()
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
//We're joining the type so we can do a query against the member type - not sure if this adds much overhead or not?
// the execution plan says it doesn't so we'll go with that and in that case, it might be worth joining the content
// types by default on the document and media repo's so we can query by content type there too.
.InnerJoin<ContentTypeDto>(SqlSyntax)
.On<ContentTypeDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.ContentTypeId)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
.InnerJoin<ContentTypeDto>().On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
.InnerJoin<NodeDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
return sql;
}
protected override Sql GetBaseQuery(bool isCount)
{
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
}
protected override string GetBaseWhereClause()
@@ -385,8 +380,8 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
return ProcessQuery(sql, new PagingSqlQuery(sql), true);
}
return ProcessQuery(sql, true);
}
public void RebuildXmlStructures(Func<IMember, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
{
@@ -408,8 +403,7 @@ namespace Umbraco.Core.Persistence.Repositories
query = query
.Where<NodeDto>(x => x.NodeId > baseId)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
var sql = SqlSyntax.SelectTop(query, groupSize);
var xmlItems = ProcessQuery(sql, new PagingSqlQuery(sql))
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
.ToList();
@@ -448,16 +442,16 @@ namespace Umbraco.Core.Persistence.Repositories
var memberType = _memberTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
var factory = new MemberFactory(memberType, NodeObjectTypeId, dto.NodeId);
var member = factory.BuildEntity(dto);
var media = factory.BuildEntity(dto);
var properties = GetPropertyCollection(new PagingSqlQuery(sql), new[] { new DocumentDefinition(dto.ContentVersionDto, memberType) });
var properties = GetPropertyCollection(sql, new[] { new DocumentDefinition(dto.NodeId, dto.ContentVersionDto.VersionId, media.UpdateDate, media.CreateDate, memberType) });
member.Properties = properties[dto.ContentVersionDto.VersionId];
media.Properties = properties[dto.NodeId];
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
((Entity)member).ResetDirtyProperties(false);
return member;
((Entity)media).ResetDirtyProperties(false);
return media;
}
@@ -541,7 +535,7 @@ namespace Umbraco.Core.Persistence.Repositories
.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(sql, new PagingSqlQuery(sql));
return ProcessQuery(sql);
}
@@ -623,9 +617,9 @@ namespace Umbraco.Core.Persistence.Repositories
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
}
return GetPagedResultsByQuery<MemberDto>(query, pageIndex, pageSize, out totalRecords,
return GetPagedResultsByQuery<MemberDto, Member>(query, pageIndex, pageSize, out totalRecords,
new Tuple<string, string>("cmsMember", "nodeId"),
(sqlFull, sqlIds) => ProcessQuery(sqlFull, sqlIds), orderBy, orderDirection, orderBySystemField,
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
filterCallback);
}
@@ -665,37 +659,25 @@ namespace Umbraco.Core.Persistence.Repositories
return base.GetEntityPropertyNameForOrderBy(orderBy);
}
/// <summary>
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The full SQL to select all member data
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL to just return all member ids - used to process the properties for the member item
/// </param>
/// <param name="withCache"></param>
/// <returns></returns>
private IEnumerable<IMember> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
private IEnumerable<IMember> ProcessQuery(Sql sql, bool withCache = false)
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sqlFull);
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sql);
//This is a tuple list identifying if the content item came from the cache or not
var content = new List<Tuple<IMember, bool>>();
var defs = new DocumentDefinitionCollection();
var content = new IMember[dtos.Count];
var defs = new List<DocumentDefinition>();
foreach (var dto in dtos)
for (var i = 0; i < dtos.Count; i++)
{
var dto = dtos[i];
// if the cache contains the item, use it
if (withCache)
{
var cached = RuntimeCache.GetCacheItem<IMember>(GetCacheIdKey<IMember>(dto.NodeId));
//only use this cached version if the dto returned is the same version - this is just a safety check, members dont
//store different versions, but just in case someone corrupts some data we'll double check to be sure.
if (cached != null && cached.Version == dto.ContentVersionDto.VersionId)
if (cached != null)
{
content.Add(new Tuple<IMember, bool>(cached, true));
content[i] = cached;
continue;
}
}
@@ -703,54 +685,60 @@ namespace Umbraco.Core.Persistence.Repositories
// else, need to fetch from the database
// content type repository is full-cache so OK to get each one independently
var contentType = _memberTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
var factory = new MemberFactory(contentType, NodeObjectTypeId, dto.NodeId);
content[i] = factory.BuildEntity(dto);
// need properties
if (defs.AddOrUpdate(new DocumentDefinition(dto.ContentVersionDto, contentType)))
{
content.Add(new Tuple<IMember, bool>(MemberFactory.BuildEntity(dto, contentType), false));
}
defs.Add(new DocumentDefinition(
dto.NodeId,
dto.ContentVersionDto.VersionId,
dto.ContentVersionDto.VersionDate,
dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
contentType
));
}
// load all properties for all documents from database in 1 query
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
var propertyData = GetPropertyCollection(sql, defs);
// assign property data
foreach (var contentItem in content)
// assign
var dtoIndex = 0;
foreach (var def in defs)
{
var cc = contentItem.Item1;
var fromCache = contentItem.Item2;
// move to corresponding item (which has to exist)
while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++;
//if this has come from cache, we do not need to build up it's structure
if (fromCache) continue;
cc.Properties = propertyData[cc.Version];
// complete the item
var cc = content[dtoIndex];
cc.Properties = propertyData[cc.Id];
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
cc.ResetDirtyProperties(false);
((Entity)cc).ResetDirtyProperties(false);
}
return content.Select(x => x.Item1).ToArray();
return content;
}
/// <summary>
/// Private method to create a member object from a MemberDto
/// </summary>
/// <param name="dto"></param>
/// <param name="versionId"></param>
/// <param name="docSql"></param>
/// <returns></returns>
private IMember CreateMemberFromDto(MemberDto dto, Sql docSql)
private IMember CreateMemberFromDto(MemberDto dto, Guid versionId, Sql docSql)
{
var memberType = _memberTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
var factory = new MemberFactory(memberType, NodeObjectTypeId, dto.ContentVersionDto.NodeId);
var member = factory.BuildEntity(dto);
var docDef = new DocumentDefinition(dto.ContentVersionDto, memberType);
var docDef = new DocumentDefinition(dto.ContentVersionDto.NodeId, versionId, member.UpdateDate, member.CreateDate, memberType);
var properties = GetPropertyCollection(docSql, new[] { docDef });
member.Properties = properties[dto.ContentVersionDto.VersionId];
member.Properties = properties[dto.ContentVersionDto.NodeId];
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -44,9 +44,10 @@ namespace Umbraco.Core.Persistence.Repositories
{
sql.Where("umbracoAccess.id IN (@ids)", new { ids = ids });
}
sql.OrderBy<AccessDto>(x => x.NodeId, SqlSyntax);
var factory = new PublicAccessEntryFactory();
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
return dtos.Select(factory.BuildEntity);
}
@@ -58,7 +59,6 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = translator.Translate();
var factory = new PublicAccessEntryFactory();
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
return dtos.Select(factory.BuildEntity);
}
@@ -69,9 +69,7 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Select("*")
.From<AccessDto>(SqlSyntax)
.LeftJoin<AccessRuleDto>(SqlSyntax)
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId)
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId);
return sql;
}
@@ -112,11 +110,6 @@ namespace Umbraco.Core.Persistence.Repositories
{
rule.AccessId = entity.Key;
Database.Insert(rule);
//update the id so HasEntity is correct
var entityRule = entity.Rules.First(x => x.Key == rule.Id);
entityRule.Id = entityRule.Key.GetHashCode();
//double make sure that this is set since it is possible to add rules via ctor without AddRule
entityRule.AccessEntryId = entity.Key;
}
entity.ResetDirtyProperties();
@@ -137,7 +130,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
if (rule.HasIdentity)
{
var count = Database.Update(dto.Rules.First(x => x.Id == rule.Key));
var count = Database.Update(dto.Rules.Single(x => x.Id == rule.Key));
if (count == 0)
{
throw new InvalidOperationException("No rows were updated for the access rule");
@@ -163,8 +156,6 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Delete<AccessRuleDto>("WHERE id=@Id", new {Id = removedRule});
}
entity.ClearRemovedRules();
entity.ResetDirtyProperties();
}
@@ -173,4 +164,4 @@ namespace Umbraco.Core.Persistence.Repositories
return entity.Key;
}
}
}
}
@@ -63,7 +63,6 @@ namespace Umbraco.Core.Persistence.Repositories
FormatDeleteStatement("cmsContentVersion", "ContentId"),
FormatDeleteStatement("cmsContentXml", "nodeId"),
FormatDeleteStatement("cmsContent", "nodeId"),
//TODO: Why is this being done? We just delete this exact data in the next line
"UPDATE umbracoNode SET parentID = '" + RecycleBinId + "' WHERE trashed = '1' AND nodeObjectType = @NodeObjectType",
"DELETE FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType"
};
@@ -92,18 +91,14 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
/// <summary>
/// A delete statement that will delete anything in the table specified where its PK (keyName) is found in the
/// list of umbracoNode.id that have trashed flag set
/// </summary>
/// <param name="tableName"></param>
/// <param name="keyName"></param>
/// <returns></returns>
private string FormatDeleteStatement(string tableName, string keyName)
{
//This query works with sql ce and sql server:
//DELETE FROM umbracoUser2NodeNotify WHERE umbracoUser2NodeNotify.nodeId IN
//(SELECT nodeId FROM umbracoUser2NodeNotify as TB1 INNER JOIN umbracoNode as TB2 ON TB1.nodeId = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972')
return
string.Format(
"DELETE FROM {0} WHERE {0}.{1} IN (SELECT id FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType)",
"DELETE FROM {0} WHERE {0}.{1} IN (SELECT TB1.{1} FROM {0} as TB1 INNER JOIN umbracoNode as TB2 ON TB1.{1} = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
tableName, keyName);
}
@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
@@ -28,16 +25,13 @@ using Umbraco.Core.IO;
namespace Umbraco.Core.Persistence.Repositories
{
using SqlSyntax;
internal abstract class VersionableRepositoryBase<TId, TEntity> : PetaPocoRepositoryBase<TId, TEntity>
where TEntity : class, IAggregateRoot
{
private readonly IContentSection _contentSection;
/// <summary>
/// This is used for unit tests ONLY
/// </summary>
internal static bool ThrowOnWarning = false;
protected VersionableRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IContentSection contentSection)
: base(work, cache, logger, sqlSyntax)
{
@@ -143,40 +137,6 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
/// <summary>
/// Gets paged document descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="orderBy"></param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
public virtual IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, string[] orderBy, out long totalRecords)
{
var query = new Sql().Select(string.Format("umbracoNode.id, cmsContentXml.{0}", SqlSyntax.GetQuotedColumnName("xml")))
.From("umbracoNode")
.InnerJoin("cmsContentXml").On("cmsContentXml.nodeId = umbracoNode.id");
if (path == "-1")
{
query.Where("umbracoNode.nodeObjectType = @type", new { type = NodeObjectTypeId });
}
else
{
query.Where(string.Format("umbracoNode.{0} LIKE (@0)", SqlSyntax.GetQuotedColumnName("path")), path.EnsureEndsWith(",%"));
}
//each order by param needs to be in a bracket! see: https://github.com/toptensoftware/PetaPoco/issues/177
query.OrderBy(orderBy == null
? "(umbracoNode.id)"
: string.Join(",", orderBy.Select(x => string.Format("({0})", SqlSyntax.GetQuotedColumnName(x)))));
var pagedResult = Database.Page<ContentXmlDto>(pageIndex + 1, pageSize, query);
totalRecords = pagedResult.TotalItems;
return pagedResult.Items.Select(dto => XElement.Parse(dto.Xml));
}
public int CountDescendants(int parentId, string contentTypeAlias = null)
{
var pathMatch = parentId == -1
@@ -388,8 +348,12 @@ namespace Umbraco.Core.Persistence.Repositories
ON CustomPropData.CustomPropValContentId = umbracoNode.id
", sortedInt, sortedDecimal, sortedDate, sortedString, nodeIdSelect.Item2, nodeIdSelect.Item1, versionQuery, sortedSql.Arguments.Length, newestQuery);
//insert this just above the last WHERE
string newSql = sortedSql.SQL.Insert(sortedSql.SQL.LastIndexOf("WHERE"), outerJoinTempTable);
//insert this just above the first LEFT OUTER JOIN (for cmsDocument) or the last WHERE (everything else)
string newSql;
if (nodeIdSelect.Item1 == "cmsDocument")
newSql = sortedSql.SQL.Insert(sortedSql.SQL.IndexOf("LEFT OUTER JOIN"), outerJoinTempTable);
else
newSql = sortedSql.SQL.Insert(sortedSql.SQL.LastIndexOf("WHERE"), outerJoinTempTable);
var newArgs = sortedSql.Arguments.ToList();
newArgs.Add(orderBy);
@@ -406,14 +370,11 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
if (orderBySystemField && orderBy != "umbracoNode.id")
{
//no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique.
// if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column
// is empty for many nodes)
// see: http://issues.umbraco.org/issue/U4-8831
sortedSql.OrderBy("umbracoNode.id");
}
//no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique.
// if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column
// is empty for many nodes)
// see: http://issues.umbraco.org/issue/U4-8831
sortedSql.OrderBy("umbracoNode.id");
return sortedSql;
@@ -423,6 +384,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// A helper method for inheritors to get the paged results by query in a way that minimizes queries
/// </summary>
/// <typeparam name="TDto">The type of the d.</typeparam>
/// <typeparam name="TContentBase">The 'true' entity type (i.e. Content, Member, etc...)</typeparam>
/// <param name="query">The query.</param>
/// <param name="pageIndex">Index of the page.</param>
/// <param name="pageSize">Size of the page.</param>
@@ -435,30 +397,34 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="orderBySystemField">Flag to indicate when ordering by system field</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">orderBy</exception>
protected IEnumerable<TEntity> GetPagedResultsByQuery<TDto>(IQuery<TEntity> query, long pageIndex, int pageSize, out long totalRecords,
protected IEnumerable<TEntity> GetPagedResultsByQuery<TDto, TContentBase>(IQuery<TEntity> query, long pageIndex, int pageSize, out long totalRecords,
Tuple<string, string> nodeIdSelect,
Func<Sql, PagingSqlQuery<TDto>, IEnumerable<TEntity>> processQuery,
Func<Sql, IEnumerable<TEntity>> processQuery,
string orderBy,
Direction orderDirection,
bool orderBySystemField,
Func<Tuple<string, object[]>> defaultFilter = null)
where TContentBase : class, IAggregateRoot, TEntity
{
if (orderBy == null) throw new ArgumentNullException("orderBy");
// Get base query for returning IDs
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
// Get base query for returning all data
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
// Get base query
var sqlBase = GetBaseQuery(false);
if (query == null) query = new Query<TEntity>();
var translatorIds = new SqlTranslator<TEntity>(sqlBaseIds, query);
var sqlQueryIds = translatorIds.Translate();
var translatorFull = new SqlTranslator<TEntity>(sqlBaseFull, query);
var sqlQueryFull = translatorFull.Translate();
var translator = new SqlTranslator<TEntity>(sqlBase, query);
var sqlQuery = translator.Translate();
// Note we can't do multi-page for several DTOs like we can multi-fetch and are doing in PerformGetByQuery,
// but actually given we are doing a Get on each one (again as in PerformGetByQuery), we only need the node Id.
// So we'll modify the SQL.
var sqlNodeIds = new Sql(
sqlQuery.SQL.Replace("SELECT *", string.Format("SELECT {0}.{1}", nodeIdSelect.Item1, nodeIdSelect.Item2)),
sqlQuery.Arguments);
//get sorted and filtered sql
var sqlNodeIdsWithSort = GetSortedSqlForPagedResults(
GetFilteredSqlForPagedResults(sqlQueryIds, defaultFilter),
GetFilteredSqlForPagedResults(sqlNodeIds, defaultFilter),
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
// Get page of results and total count
@@ -467,32 +433,38 @@ namespace Umbraco.Core.Persistence.Repositories
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
//NOTE: We need to check the actual items returned, not the 'totalRecords', that is because if you request a page number
// that doesn't actually have any data on it, the totalRecords will still indicate there are records but there are none in
// that doesn't actually have any data on it, the totalRecords will still indicate there are records but there are none in
// the pageResult, then the GetAll will actually return ALL records in the db.
if (pagedResult.Items.Any())
{
//Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
//Crete the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
var args = sqlNodeIdsWithSort.Arguments;
string sqlStringCount, sqlStringPage;
Database.BuildPageQueries<TDto>(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage);
//We need to make this FULL query an inner join on the paged ID query
var splitQuery = sqlQueryFull.SQL.Split(new[] { "WHERE " }, StringSplitOptions.None);
var fullQueryWithPagedInnerJoin = new Sql(splitQuery[0])
//if this is for sql server, the sqlPage will start with a SELECT * but we don't want that, we only want to return the nodeId
sqlStringPage = sqlStringPage
.Replace("SELECT *",
//This ensures we only take the field name of the node id select and not the table name - since the resulting select
// will ony work with the field name.
"SELECT " + nodeIdSelect.Item2);
//We need to make this an inner join on the paged query
var splitQuery = sqlQuery.SQL.Split(new[] { "WHERE " }, StringSplitOptions.None);
var withInnerJoinSql = new Sql(splitQuery[0])
.Append("INNER JOIN (")
//join the paged query with the paged query arguments
.Append(sqlStringPage, args)
.Append(") temp ")
.Append(string.Format("ON {0}.{1} = temp.{1}", nodeIdSelect.Item1, nodeIdSelect.Item2))
//add the original where clause back with the original arguments
.Where(splitQuery[1], sqlQueryIds.Arguments);
.Where(splitQuery[1], sqlQuery.Arguments);
//get sorted and filtered sql
var fullQuery = GetSortedSqlForPagedResults(
GetFilteredSqlForPagedResults(fullQueryWithPagedInnerJoin, defaultFilter),
GetFilteredSqlForPagedResults(withInnerJoinSql, defaultFilter),
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
return processQuery(fullQuery, new PagingSqlQuery<TDto>(Database, sqlNodeIdsWithSort, pageIndex, pageSize));
return processQuery(fullQuery);
}
else
{
@@ -502,78 +474,21 @@ namespace Umbraco.Core.Persistence.Repositories
return result;
}
/// <summary>
/// Gets the property collection for a non-paged query
/// </summary>
/// <param name="sql"></param>
/// <param name="documentDefs"></param>
/// <returns></returns>
protected IDictionary<Guid, PropertyCollection> GetPropertyCollection(
Sql sql,
IReadOnlyCollection<DocumentDefinition> documentDefs)
protected IDictionary<int, PropertyCollection> GetPropertyCollection(
Sql docSql,
IEnumerable<DocumentDefinition> documentDefs)
{
return GetPropertyCollection(new PagingSqlQuery(sql), documentDefs);
}
if (documentDefs.Any() == false) return new Dictionary<int, PropertyCollection>();
/// <summary>
/// Gets the property collection for a query
/// </summary>
/// <param name="pagingSqlQuery"></param>
/// <param name="documentDefs"></param>
/// <returns></returns>
protected IDictionary<Guid, PropertyCollection> GetPropertyCollection(
PagingSqlQuery pagingSqlQuery,
IReadOnlyCollection<DocumentDefinition> documentDefs)
{
if (documentDefs.Count == 0) return new Dictionary<Guid, PropertyCollection>();
//initialize to the query passed in
var docSql = pagingSqlQuery.PrePagedSql;
//we need to parse the original SQL statement and reduce the columns to just cmsContent.nodeId, cmsContentVersion.VersionId so that we can use
//we need to parse the original SQL statement and reduce the columns to just cmsContent.nodeId, cmsContentVersion.VersionId so that we can use
// the statement to go get the property data for all of the items by using an inner join
var parsedOriginalSql = "SELECT {0} " + docSql.SQL.Substring(docSql.SQL.IndexOf("FROM", StringComparison.Ordinal));
if (pagingSqlQuery.HasPaging)
//now remove everything from an Orderby clause and beyond
if (parsedOriginalSql.InvariantContains("ORDER BY "))
{
//if this is a paged query, build the paged query with the custom column substitution, then re-assign
docSql = pagingSqlQuery.BuildPagedQuery("{0}");
parsedOriginalSql = docSql.SQL;
}
else if (parsedOriginalSql.InvariantContains("ORDER BY "))
{
//now remove everything from an Orderby clause and beyond if this is unpaged data
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
}
//This retrieves all pre-values for all data types that are referenced for all property types
// that exist in the data set.
//Benchmarks show that eagerly loading these so that we can lazily read the property data
// below (with the use of Query intead of Fetch) go about 30% faster, so we'll eagerly load
// this now since we cannot execute another reader inside of reading the property data.
var preValsSql = new Sql(@"SELECT a.id, a.value, a.sortorder, a.alias, a.datatypeNodeId
FROM cmsDataTypePreValues a
WHERE EXISTS(
SELECT DISTINCT b.id as preValIdInner
FROM cmsDataTypePreValues b
INNER JOIN cmsPropertyType
ON b.datatypeNodeId = cmsPropertyType.dataTypeId
INNER JOIN
(" + string.Format(parsedOriginalSql, "cmsContent.contentType") + @") as docData
ON cmsPropertyType.contentTypeId = docData.contentType
WHERE a.id = b.id)", docSql.Arguments);
var allPreValues = Database.Fetch<DataTypePreValueDto>(preValsSql);
//It's Important with the sort order here! We require this to be sorted by node id,
// this is required because this data set can be huge depending on the page size. Due
// to it's size we need to be smart about iterating over the property values to build
// the document. Before we used to use Linq to get the property data for a given content node
// and perform a Distinct() call. This kills performance because that would mean if we had 7000 nodes
// and on each iteration we will perform a lookup on potentially 100,000 property rows against the node
// id which turns out to be a crazy amount of iterations. Instead we know it's sorted by this value we'll
// keep an index stored of the rows being read so we never have to re-iterate the entire data set
// on each document iteration.
var propSql = new Sql(@"SELECT cmsPropertyData.*
FROM cmsPropertyData
INNER JOIN cmsPropertyType
@@ -581,64 +496,46 @@ ON cmsPropertyData.propertytypeid = cmsPropertyType.id
INNER JOIN
(" + string.Format(parsedOriginalSql, "cmsContent.nodeId, cmsContentVersion.VersionId") + @") as docData
ON cmsPropertyData.versionId = docData.VersionId AND cmsPropertyData.contentNodeId = docData.nodeId
ORDER BY contentNodeId, versionId, propertytypeid
", docSql.Arguments);
//This does NOT fetch all data into memory in a list, this will read
// over the records as a data reader, this is much better for performance and memory,
// but it means that during the reading of this data set, nothing else can be read
// from SQL server otherwise we'll get an exception.
var allPropertyData = Database.Query<PropertyDataDto>(propSql);
LEFT OUTER JOIN cmsDataTypePreValues
ON cmsPropertyType.dataTypeId = cmsDataTypePreValues.datatypeNodeId", docSql.Arguments);
var result = new Dictionary<Guid, PropertyCollection>();
var propertiesWithTagSupport = new Dictionary<string, SupportTagsAttribute>();
//used to track the resolved composition property types per content type so we don't have to re-resolve (ToArray) the list every time
var resolvedCompositionProperties = new Dictionary<int, PropertyType[]>();
var allPropertyData = Database.Fetch<PropertyDataDto>(propSql);
//keep track of the current property data item being enumerated
var propertyDataSetEnumerator = allPropertyData.GetEnumerator();
var hasCurrent = false; // initially there is no enumerator.Current
var comparer = new DocumentDefinitionComparer(SqlSyntax);
try
//This is a lazy access call to get all prevalue data for the data types that make up all of these properties which we use
// below if any property requires tag support
var allPreValues = new Lazy<IEnumerable<DataTypePreValueDto>>(() =>
{
//This must be sorted by node id because this is how we are sorting the query to lookup property types above,
// which allows us to more efficiently iterate over the large data set of property values
foreach (var def in documentDefs.OrderBy(x => x.Id).ThenBy(x => x.Version, comparer))
var preValsSql = new Sql(@"SELECT a.id, a.value, a.sortorder, a.alias, a.datatypeNodeId
FROM cmsDataTypePreValues a
WHERE EXISTS(
SELECT DISTINCT b.id as preValIdInner
FROM cmsDataTypePreValues b
INNER JOIN cmsPropertyType
ON b.datatypeNodeId = cmsPropertyType.dataTypeId
INNER JOIN
(" + string.Format(parsedOriginalSql, "DISTINCT cmsContent.contentType") + @") as docData
ON cmsPropertyType.contentTypeId = docData.contentType
WHERE a.id = b.id)", docSql.Arguments);
return Database.Fetch<DataTypePreValueDto>(preValsSql);
});
var result = new Dictionary<int, PropertyCollection>();
var propertiesWithTagSupport = new Dictionary<string, SupportTagsAttribute>();
//iterate each definition grouped by it's content type - this will mean less property type iterations while building
// up the property collections
foreach (var compositionGroup in documentDefs.GroupBy(x => x.Composition))
{
var compositionProperties = compositionGroup.Key.CompositionPropertyTypes.ToArray();
foreach (var def in compositionGroup)
{
// get the resolved properties from our local cache, or resolve them and put them in cache
PropertyType[] compositionProperties;
if (resolvedCompositionProperties.ContainsKey(def.Composition.Id))
{
compositionProperties = resolvedCompositionProperties[def.Composition.Id];
}
else
{
compositionProperties = def.Composition.CompositionPropertyTypes.ToArray();
resolvedCompositionProperties[def.Composition.Id] = compositionProperties;
}
var propertyDataDtos = allPropertyData.Where(x => x.NodeId == def.Id).Distinct();
// assemble the dtos for this def
// use the available enumerator.Current if any else move to next
var propertyDataDtos = new List<PropertyDataDto>();
while (hasCurrent || propertyDataSetEnumerator.MoveNext())
{
//Not checking null on VersionId because it can never be null - no idea why it's set to nullable
// ReSharper disable once PossibleInvalidOperationException
if (propertyDataSetEnumerator.Current.VersionId.Value == def.Version)
{
hasCurrent = false; // enumerator.Current is not available
propertyDataDtos.Add(propertyDataSetEnumerator.Current);
}
else
{
hasCurrent = true; // enumerator.Current is available for another def
break; // no more propertyDataDto for this def
}
}
var properties = PropertyFactory.BuildEntity(propertyDataDtos, compositionProperties, def.CreateDate, def.VersionDate).ToArray();
var propertyFactory = new PropertyFactory(compositionProperties, def.Version, def.Id, def.CreateDate, def.VersionDate);
var properties = propertyFactory.BuildEntity(propertyDataDtos.ToArray()).ToArray();
foreach (var property in properties)
{
@@ -655,7 +552,7 @@ ORDER BY contentNodeId, versionId, propertytypeid
propertiesWithTagSupport[property.PropertyType.PropertyEditorAlias] = tagSupport;
//this property has tags, so we need to extract them and for that we need the prevals which we've already looked up
var preValData = allPreValues.Where(x => x.DataTypeNodeId == property.PropertyType.DataTypeDefinitionId)
var preValData = allPreValues.Value.Where(x => x.DataTypeNodeId == property.PropertyType.DataTypeDefinitionId)
.Distinct()
.ToArray();
@@ -671,30 +568,38 @@ ORDER BY contentNodeId, versionId, propertytypeid
}
}
if (result.ContainsKey(def.Version))
if (result.ContainsKey(def.Id))
{
var msg = string.Format("The query returned multiple property sets for document definition {0}, {1}, {2}", def.Id, def.Version, def.Composition.Name);
if (ThrowOnWarning)
{
throw new InvalidOperationException(msg);
}
else
{
Logger.Warn<VersionableRepositoryBase<TId, TEntity>>(msg);
}
Logger.Warn<VersionableRepositoryBase<TId, TEntity>>("The query returned multiple property sets for document definition " + def.Id + ", " + def.Composition.Name);
}
result[def.Version] = new PropertyCollection(properties);
result[def.Id] = new PropertyCollection(properties);
}
}
finally
{
propertyDataSetEnumerator.Dispose();
}
return result;
}
public class DocumentDefinition
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public DocumentDefinition(int id, Guid version, DateTime versionDate, DateTime createDate, IContentTypeComposition composition)
{
Id = id;
Version = version;
VersionDate = versionDate;
CreateDate = createDate;
Composition = composition;
}
public int Id { get; set; }
public Guid Version { get; set; }
public DateTime VersionDate { get; set; }
public DateTime CreateDate { get; set; }
public IContentTypeComposition Composition { get; set; }
}
protected virtual string GetDatabaseFieldNameForOrderBy(string orderBy)
{
// Translate the passed order by field (which were originally defined for in-memory object sorting
@@ -706,8 +611,8 @@ ORDER BY contentNodeId, versionId, propertytypeid
case "NAME":
return "umbracoNode.text";
case "PUBLISHED":
case "OWNER":
return "cmsDocument.published";
case "OWNER":
//TODO: This isn't going to work very nicely because it's going to order by ID, not by letter
return "umbracoNode.nodeUser";
// Members only
@@ -783,233 +688,5 @@ ORDER BY contentNodeId, versionId, propertytypeid
return allsuccess;
}
/// <summary>
/// For Paging, repositories must support returning different query for the query type specified
/// </summary>
/// <param name="queryType"></param>
/// <returns></returns>
protected abstract Sql GetBaseQuery(BaseQueryType queryType);
internal class DocumentDefinitionCollection : KeyedCollection<ValueType, DocumentDefinition>
{
private readonly bool _includeAllVersions;
/// <summary>
/// Constructor specifying if all versions should be allowed, in that case the key for the collection becomes the versionId (GUID)
/// </summary>
/// <param name="includeAllVersions"></param>
public DocumentDefinitionCollection(bool includeAllVersions = false)
{
_includeAllVersions = includeAllVersions;
}
protected override ValueType GetKeyForItem(DocumentDefinition item)
{
return _includeAllVersions ? (ValueType)item.Version : item.Id;
}
/// <summary>
/// if this key already exists if it does then we need to check
/// if the existing item is 'older' than the new item and if that is the case we'll replace the older one
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool AddOrUpdate(DocumentDefinition item)
{
//if we are including all versions then just add, we aren't checking for latest
if (_includeAllVersions)
{
base.Add(item);
return true;
}
if (Dictionary == null)
{
base.Add(item);
return true;
}
var key = GetKeyForItem(item);
DocumentDefinition found;
if (TryGetValue(key, out found))
{
//it already exists and it's older so we need to replace it
if (item.VersionId > found.VersionId)
{
var currIndex = Items.IndexOf(found);
if (currIndex == -1)
throw new IndexOutOfRangeException("Could not find the item in the list: " + found.Version);
//replace the current one with the newer one
SetItem(currIndex, item);
return true;
}
//could not add or update
return false;
}
base.Add(item);
return true;
}
public bool TryGetValue(ValueType key, out DocumentDefinition val)
{
if (Dictionary == null)
{
val = null;
return false;
}
return Dictionary.TryGetValue(key, out val);
}
}
/// <summary>
/// A custom comparer required for sorting entities by GUIDs to match how the sorting of GUIDs works on SQL server
/// </summary>
/// <remarks>
/// MySql sorts GUIDs as a string, MSSQL sorts based on byte sections, this comparer will allow sorting GUIDs to be the same as how SQL server does
/// </remarks>
private class DocumentDefinitionComparer : IComparer<Guid>
{
private readonly ISqlSyntaxProvider _sqlSyntax;
public DocumentDefinitionComparer(ISqlSyntaxProvider sqlSyntax)
{
_sqlSyntax = sqlSyntax;
}
public int Compare(Guid x, Guid y)
{
//MySql sorts on GUIDs as strings (i.e. normal)
if (_sqlSyntax is MySqlSyntaxProvider)
{
return x.CompareTo(y);
}
//MSSQL doesn't it sorts them on byte sections!
return new SqlGuid(x).CompareTo(new SqlGuid(y));
}
}
internal class DocumentDefinition
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public DocumentDefinition(DocumentDto dto, IContentTypeComposition composition)
{
DocumentDto = dto;
ContentVersionDto = dto.ContentVersionDto;
Composition = composition;
}
public DocumentDefinition(ContentVersionDto dto, IContentTypeComposition composition)
{
ContentVersionDto = dto;
Composition = composition;
}
public DocumentDto DocumentDto { get; private set; }
public ContentVersionDto ContentVersionDto { get; private set; }
public int Id
{
get { return ContentVersionDto.NodeId; }
}
public Guid Version
{
get { return DocumentDto != null ? DocumentDto.VersionId : ContentVersionDto.VersionId; }
}
/// <summary>
/// This is used to determien which version is the most recent
/// </summary>
public int VersionId
{
get { return ContentVersionDto.Id; }
}
public DateTime VersionDate
{
get { return ContentVersionDto.VersionDate; }
}
public DateTime CreateDate
{
get { return ContentVersionDto.ContentDto.NodeDto.CreateDate; }
}
public IContentTypeComposition Composition { get; set; }
}
/// <summary>
/// An object representing a query that may contain paging information
/// </summary>
internal class PagingSqlQuery
{
public Sql PrePagedSql { get; private set; }
public PagingSqlQuery(Sql prePagedSql)
{
PrePagedSql = prePagedSql;
}
public virtual bool HasPaging
{
get { return false; }
}
public virtual Sql BuildPagedQuery(string selectColumns)
{
throw new InvalidOperationException("This query has no paging information");
}
}
/// <summary>
/// An object representing a query that contains paging information
/// </summary>
/// <typeparam name="T"></typeparam>
internal class PagingSqlQuery<T> : PagingSqlQuery
{
private readonly Database _db;
private readonly long _pageIndex;
private readonly int _pageSize;
public PagingSqlQuery(Database db, Sql prePagedSql, long pageIndex, int pageSize) : base(prePagedSql)
{
_db = db;
_pageIndex = pageIndex;
_pageSize = pageSize;
}
public override bool HasPaging
{
get { return _pageSize > 0; }
}
/// <summary>
/// Creates a paged query based on the original query and subtitutes the selectColumns specified
/// </summary>
/// <param name="selectColumns"></param>
/// <returns></returns>
public override Sql BuildPagedQuery(string selectColumns)
{
if (HasPaging == false) throw new InvalidOperationException("This query has no paging information");
var resultSql = string.Format("SELECT {0} {1}", selectColumns, PrePagedSql.SQL.Substring(PrePagedSql.SQL.IndexOf("FROM", StringComparison.Ordinal)));
//this query is meant to be paged so we need to generate the paging syntax
//Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
var args = PrePagedSql.Arguments;
string sqlStringCount, sqlStringPage;
_db.BuildPageQueries<T>(_pageIndex * _pageSize, _pageSize, resultSql, ref args, out sqlStringCount, out sqlStringPage);
return new Sql(sqlStringPage, args);
}
}
}
}
@@ -114,7 +114,7 @@ namespace Umbraco.Core.Security
AllowRefresh = true,
IssuedUtc = nowUtc,
ExpiresUtc = nowUtc.AddMinutes(GlobalSettings.TimeOutInMinutes)
}, userIdentity, rememberBrowserIdentity);
}, userIdentity, rememberBrowserIdentity);
}
else
{
@@ -127,10 +127,6 @@ namespace Umbraco.Core.Security
}, userIdentity);
}
//track the last login date
user.LastLoginDateUtc = DateTime.UtcNow;
await UserManager.UpdateAsync(user);
_logger.WriteCore(TraceEventType.Information, 0,
string.Format(
"Login attempt succeeded for username {0} from IP address {1}",
@@ -625,12 +625,6 @@ namespace Umbraco.Core.Security
var anythingChanged = false;
//don't assign anything if nothing has changed as this will trigger
//the track changes of the model
if ((user.LastLoginDate != default(DateTime) && identityUser.LastLoginDateUtc.HasValue == false)
|| identityUser.LastLoginDateUtc.HasValue && user.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value)
{
anythingChanged = true;
user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime();
}
if (user.Name != identityUser.Name && identityUser.Name.IsNullOrWhiteSpace() == false)
{
anythingChanged = true;
+2 -33
View File
@@ -585,7 +585,7 @@ namespace Umbraco.Core.Services
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
public IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
{
long total;
var result = GetPagedDescendants(id, Convert.ToInt64(pageIndex), pageSize, out total, orderBy, orderDirection, true, filter);
@@ -604,7 +604,7 @@ namespace Umbraco.Core.Services
/// <param name="orderDirection">Direction to order by</param>
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
public IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
{
return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter);
}
@@ -1764,31 +1764,6 @@ namespace Umbraco.Core.Services
return true;
}
/// <summary>
/// Gets paged content descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
public IEnumerable<XElement> GetPagedXmlEntries(string path, long pageIndex, int pageSize, out long totalRecords)
{
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentRepository(uow))
{
var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize,
//This order by is VERY important! This allows us to figure out what is implicitly not published, see ContentRepository.BuildXmlCache and
// UmbracoContentIndexer.PerformIndexAll which uses the logic based on this sort order
new[] {"level", "parentID", "sortOrder"},
out totalRecords);
return contents;
}
}
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
@@ -2185,7 +2160,6 @@ namespace Umbraco.Core.Services
//We need to check if children and their publish state to ensure that we 'republish' content that was previously published
if (published && previouslyPublished == false && HasChildren(content.Id))
{
//TODO: Horrible for performance if there are lots of descendents! We should page if anything but this is crazy
var descendants = GetPublishedDescendants(content);
_publishingStrategy.PublishingFinalized(descendants, false);
@@ -2218,11 +2192,6 @@ namespace Umbraco.Core.Services
}
}
if (string.IsNullOrWhiteSpace(content.Name))
{
throw new ArgumentException("Cannot save content with empty name.");
}
using (new WriteLock(Locker))
{
var uow = UowProvider.GetUnitOfWork();
@@ -719,13 +719,8 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional id of the user saving the ContentType</param>
public void Save(IContentType contentType, int userId = 0)
{
if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs<IContentType>(contentType), this))
return;
if (string.IsNullOrWhiteSpace(contentType.Name))
{
throw new ArgumentException("Cannot save content type with empty name.");
}
if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs<IContentType>(contentType), this))
return;
using (new WriteLock(Locker))
{
+2 -7
View File
@@ -361,13 +361,8 @@ namespace Umbraco.Core.Services
/// <param name="userId">Id of the user issueing the save</param>
public void Save(IDataTypeDefinition dataTypeDefinition, int userId = 0)
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition), this))
return;
if (string.IsNullOrWhiteSpace(dataTypeDefinition.Name))
{
throw new ArgumentException("Cannot save datatype with empty name.");
}
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition), this))
return;
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow))
+2 -16
View File
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
@@ -96,19 +95,6 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IContentService : IService
{
/// <summary>
/// Gets all XML entries found in the cmsContentXml table based on the given path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
/// <remarks>
/// If -1 is passed, then this will return all content xml entries, otherwise will return all descendents from the path
/// </remarks>
IEnumerable<XElement> GetPagedXmlEntries(string path, long pageIndex, int pageSize, out long totalRecords);
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
@@ -259,7 +245,7 @@ namespace Umbraco.Core.Services
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
@@ -273,7 +259,7 @@ namespace Umbraco.Core.Services
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
+2 -19
View File
@@ -80,7 +80,6 @@ namespace Umbraco.Core.Services
/// </param>
void RebuildXmlStructures(params int[] contentTypeIds);
int CountNotTrashed(string contentTypeAlias = null);
int Count(string contentTypeAlias = null);
int CountChildren(int parentId, string contentTypeAlias = null);
int CountDescendants(int parentId, string contentTypeAlias = null);
@@ -167,26 +166,10 @@ namespace Umbraco.Core.Services
IEnumerable<IMedia> GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, bool orderBySystemField, string filter);
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Children from</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records query would return without paging</param>
/// <param name="orderBy">Field to order by</param>
/// <param name="orderDirection">Direction to order by</param>
/// <param name="orderBySystemField">Flag to indicate when ordering by system field</param>
/// <param name="filter">Search text filter</param>
/// <param name="contentTypeFilter">A list of content type Ids to filter the list by</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IMedia> GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, bool orderBySystemField, string filter, int[] contentTypeFilter);
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
@@ -200,7 +183,7 @@ namespace Umbraco.Core.Services
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
@@ -14,15 +13,6 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IMemberService : IMembershipMemberService
{
/// <summary>
/// Gets all XML entries found in the cmsContentXml table
/// </summary>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
IEnumerable<XElement> GetPagedXmlEntries(long pageIndex, int pageSize, out long totalRecords);
/// <summary>
/// Rebuilds all xml content in the cmsContentXml table for all documents
/// </summary>
+11 -16
View File
@@ -142,24 +142,19 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the user deleting the macro</param>
public void Save(IMacro macro, int userId = 0)
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMacro>(macro), this))
return;
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMacro>(macro), this))
return;
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMacroRepository(uow))
{
repository.AddOrUpdate(macro);
uow.Commit();
if (string.IsNullOrWhiteSpace(macro.Name))
{
throw new ArgumentException("Cannot save macro with empty name.");
}
Saved.RaiseEvent(new SaveEventArgs<IMacro>(macro, false), this);
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMacroRepository(uow))
{
repository.AddOrUpdate(macro);
uow.Commit();
Saved.RaiseEvent(new SaveEventArgs<IMacro>(macro, false), this);
}
Audit(AuditType.Save, "Save Macro performed by user", userId, -1);
Audit(AuditType.Save, "Save Macro performed by user", userId, -1);
}
///// <summary>
+6 -54
View File
@@ -7,6 +7,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Linq;
using Umbraco.Core.Auditing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
@@ -16,7 +17,9 @@ using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Publishing;
namespace Umbraco.Core.Services
{
@@ -245,29 +248,6 @@ namespace Umbraco.Core.Services
}
}
public int CountNotTrashed(string contentTypeAlias = null)
{
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
using (var mediaTypeRepository = RepositoryFactory.CreateMediaTypeRepository(uow))
{
var mediaTypeId = 0;
if (contentTypeAlias.IsNullOrWhiteSpace() == false)
{
var mediaType = mediaTypeRepository.Get(contentTypeAlias);
if (mediaType == null) return 0;
mediaTypeId = mediaType.Id;
}
var query = Query<IMedia>.Builder.Where(media => media.Trashed == false);
if (mediaTypeId > 0)
{
query.Where(media => media.ContentTypeId == mediaTypeId);
}
return repository.Count(query);
}
}
public int Count(string contentTypeAlias = null)
{
var uow = UowProvider.GetUnitOfWork();
@@ -455,25 +435,6 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IMedia> GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren,
string orderBy, Direction orderDirection, bool orderBySystemField, string filter)
{
return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter, null);
}
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Children from</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalChildren">Total records query would return without paging</param>
/// <param name="orderBy">Field to order by</param>
/// <param name="orderDirection">Direction to order by</param>
/// <param name="orderBySystemField">Flag to indicate when ordering by system field</param>
/// <param name="filter">Search text filter</param>
/// <param name="contentTypeFilter">A list of content type Ids to filter the list by</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IMedia> GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren,
string orderBy, Direction orderDirection, bool orderBySystemField, string filter, int[] contentTypeFilter)
{
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
@@ -482,11 +443,6 @@ namespace Umbraco.Core.Services
var query = Query<IMedia>.Builder;
query.Where(x => x.ParentId == id);
if (contentTypeFilter != null && contentTypeFilter.Length > 0)
{
query.Where(x => contentTypeFilter.Contains(x.ContentTypeId));
}
var medias = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
return medias;
@@ -495,7 +451,7 @@ namespace Umbraco.Core.Services
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
public IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
{
long total;
var result = GetPagedDescendants(id, Convert.ToInt64(pageIndex), pageSize, out total, orderBy, orderDirection, true, filter);
@@ -514,7 +470,7 @@ namespace Umbraco.Core.Services
/// <param name="orderDirection">Direction to order by</param>
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
public IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
{
return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter);
}
@@ -881,11 +837,7 @@ namespace Umbraco.Core.Services
{
return OperationStatus.Cancelled(evtMsgs);
}
}
if (string.IsNullOrWhiteSpace(media.Name))
{
throw new ArgumentException("Cannot save media with empty name.");
}
var uow = UowProvider.GetUnitOfWork();
@@ -1272,7 +1224,7 @@ namespace Umbraco.Core.Services
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
{
var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, null, out totalRecords);
var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, out totalRecords);
return contents;
}
}
@@ -612,26 +612,6 @@ namespace Umbraco.Core.Services
Audit(AuditType.Publish, "MemberService.RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
}
/// <summary>
/// Gets paged member descendants as XML by path
/// </summary>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of member items</returns>
public IEnumerable<XElement> GetPagedXmlEntries(long pageIndex, int pageSize, out long totalRecords)
{
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMemberRepository(uow))
{
var contents = repository.GetPagedXmlEntriesByPath("-1", pageIndex, pageSize, null, out totalRecords);
return contents;
}
}
#endregion
#region IMembershipMemberService Implementation
@@ -998,11 +978,6 @@ namespace Umbraco.Core.Services
}
}
if (string.IsNullOrWhiteSpace(entity.Name))
{
throw new ArgumentException("Cannot save member with empty name.");
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMemberRepository(uow))
{
@@ -78,11 +78,6 @@ namespace Umbraco.Core.Services
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMemberType>(memberType), this))
return;
if (string.IsNullOrWhiteSpace(memberType.Name))
{
throw new ArgumentException("Cannot save MemberType with empty name.");
}
using (new WriteLock(Locker))
{
var uow = UowProvider.GetUnitOfWork();
-22
View File
@@ -125,11 +125,6 @@ namespace Umbraco.Core.Services
{
if (userType == null) throw new ArgumentNullException("userType");
if (string.IsNullOrWhiteSpace(username))
{
throw new ArgumentException("Cannot create user with empty username.");
}
//TODO: PUT lock here!!
var uow = UowProvider.GetUnitOfWork();
@@ -317,15 +312,6 @@ namespace Umbraco.Core.Services
return;
}
if (string.IsNullOrWhiteSpace(entity.Username))
{
throw new ArgumentException("Cannot save user with empty username.");
}
if (string.IsNullOrWhiteSpace(entity.Name))
{
throw new ArgumentException("Cannot save user with empty name.");
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateUserRepository(uow))
{
@@ -367,14 +353,6 @@ namespace Umbraco.Core.Services
{
foreach (var member in entities)
{
if (string.IsNullOrWhiteSpace(member.Username))
{
throw new ArgumentException("Cannot save user with empty username.");
}
if (string.IsNullOrWhiteSpace(member.Name))
{
throw new ArgumentException("Cannot save user with empty name.");
}
repository.AddOrUpdate(member);
}
//commit the whole lot in one go
+24
View File
@@ -0,0 +1,24 @@
namespace Umbraco.Core.Sync
{
/// <summary>
/// The type of Cold Boot taking place
/// </summary>
public enum ColdBootType
{
/// <summary>
/// Indicates there was no cold boot
/// </summary>
NoColdBoot,
/// <summary>
/// This indicates that there is no lasysynced file for the current machine/appid
/// </summary>
NeverSynced,
/// <summary>
/// This indicates that the number of instructions needing to be processed exceeds the value stored
/// for the <see cref="DatabaseServerMessengerOptions.MaxProcessingInstructionCount"/>
/// </summary>
ExceedsMaxProcessingInstructionCount
}
}
@@ -43,6 +43,11 @@ namespace Umbraco.Core.Sync
protected DatabaseServerMessengerOptions Options { get; private set; }
protected ApplicationContext ApplicationContext { get { return _appContext; } }
/// <summary>
/// Returns the cold boot type detected for the current app domain
/// </summary>
public ColdBootType ColdBootType { get; private set; }
public DatabaseServerMessenger(ApplicationContext appContext, bool distributedEnabled, DatabaseServerMessengerOptions options)
: base(distributedEnabled)
{
@@ -146,11 +151,12 @@ namespace Umbraco.Core.Sync
/// </remarks>
private void Initialize()
{
ColdBootType = ColdBootType.NoColdBoot;
lock (_locko)
{
if (_released) return;
var coldboot = false;
if (_lastId < 0) // never synced before
{
// we haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new
@@ -159,7 +165,7 @@ namespace Umbraco.Core.Sync
+ " The server will build its caches and indexes, and then adjust its last synced Id to the latest found in"
+ " the database and maintain cache updates based on that Id.");
coldboot = true;
ColdBootType = ColdBootType.NeverSynced;
}
else
{
@@ -175,11 +181,11 @@ namespace Umbraco.Core.Sync
+ " to the latest found in the database and maintain cache updates based on that Id.",
() => count, () => Options.MaxProcessingInstructionCount);
coldboot = true;
ColdBootType = ColdBootType.ExceedsMaxProcessingInstructionCount;
}
}
if (coldboot)
if (ColdBootType != ColdBootType.NoColdBoot)
{
// go get the last id in the db and store it
// note: do it BEFORE initializing otherwise some instructions might get lost
+4 -6
View File
@@ -49,9 +49,8 @@
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="ImageProcessor, Version=2.5.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.2\lib\net45\ImageProcessor.dll</HintPath>
<Private>True</Private>
<Reference Include="ImageProcessor, Version=2.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.1\lib\net45\ImageProcessor.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=1.2.11.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll</HintPath>
@@ -179,7 +178,6 @@
<Compile Include="Configuration\BaseRest\ExtensionElement.cs" />
<Compile Include="Configuration\BaseRest\ExtensionElementCollection.cs" />
<Compile Include="Configuration\BaseRest\MethodElement.cs" />
<Compile Include="Configuration\ContentXmlStorage.cs" />
<Compile Include="Configuration\Dashboard\AccessElement.cs" />
<Compile Include="Configuration\Dashboard\AccessItem.cs" />
<Compile Include="Configuration\Dashboard\AccessType.cs" />
@@ -465,11 +463,9 @@
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeOne\UpdateUserLanguagesToIsoCode.cs" />
<Compile Include="Persistence\PocoDataDataReader.cs" />
<Compile Include="Persistence\Querying\QueryExtensions.cs" />
<Compile Include="Persistence\Querying\SqlExpressionExtensions.cs" />
<Compile Include="Persistence\RecordPersistenceType.cs" />
<Compile Include="Persistence\Relators\AccessRulesRelator.cs" />
<Compile Include="Persistence\Repositories\AuditRepository.cs" />
<Compile Include="Persistence\Repositories\BaseQueryType.cs" />
<Compile Include="Persistence\Repositories\EntityContainerRepository.cs" />
<Compile Include="Persistence\Repositories\DomainRepository.cs" />
<Compile Include="Persistence\Repositories\ExternalLoginRepository.cs" />
@@ -651,6 +647,7 @@
<Compile Include="PropertyEditors\DefaultPropertyValueConverterAttribute.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSeven\UpdateRelatedLinksData.cs" />
<Compile Include="PropertyEditors\IValueEditor.cs" />
<Compile Include="Persistence\Querying\SqlStringExtensions.cs" />
<Compile Include="Persistence\Querying\StringPropertyMatchType.cs" />
<Compile Include="Persistence\Querying\TextColumnType.cs" />
<Compile Include="Persistence\Querying\ValuePropertyMatchType.cs" />
@@ -1314,6 +1311,7 @@
<Compile Include="Strings\ContentBaseExtensions.cs" />
<Compile Include="Strings\Diff.cs" />
<Compile Include="Sync\BatchedWebServiceServerMessenger.cs" />
<Compile Include="Sync\ColdBootType.cs" />
<Compile Include="Sync\IServerRegistrar2.cs" />
<Compile Include="Sync\ServerRole.cs" />
<Compile Include="Sync\DatabaseServerMessenger.cs" />
+26
View File
@@ -16,6 +16,32 @@ namespace Umbraco.Core
/// </summary>
internal static class XmlExtensions
{
/// <summary>
/// Saves the xml document async
/// </summary>
/// <param name="xdoc"></param>
/// <param name="filename"></param>
/// <returns></returns>
public static async Task SaveAsync(this XmlDocument xdoc, string filename)
{
if (xdoc.DocumentElement == null)
throw new XmlException("Cannot save xml document, there is no root element");
using (var fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true))
using (var xmlWriter = XmlWriter.Create(fs, new XmlWriterSettings
{
Async = true,
Encoding = Encoding.UTF8,
Indent = true
}))
{
//NOTE: There are no nice methods to write it async, only flushing it async. We
// could implement this ourselves but it'd be a very manual process.
xdoc.WriteTo(xmlWriter);
await xmlWriter.FlushAsync().ConfigureAwait(false);
}
}
public static bool HasAttribute(this XmlAttributeCollection attributes, string attributeName)
{
return attributes.Cast<XmlAttribute>().Any(x => x.Name == attributeName);
+1 -1
View File
@@ -2,7 +2,7 @@
<packages>
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.9" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.2" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.1" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.Owin" version="2.2.1" targetFramework="net45" />
@@ -16,7 +16,6 @@ using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Validators;
using Moq;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
@@ -44,18 +43,15 @@ namespace Umbraco.Tests.Benchmarks
public ModelToSqlExpressionHelperBenchmarks()
{
var contentMapper = new ContentMapper(_syntaxProvider);
contentMapper.BuildMap();
_contentMapper = new ContentMapper(_syntaxProvider);
_contentMapper.BuildMap();
_cachedExpression = new CachedExpression();
var mappingResolver = new Mock<MappingResolver>();
mappingResolver.Setup(resolver => resolver.ResolveMapperByType(It.IsAny<Type>())).Returns(contentMapper);
_mappingResolver = mappingResolver.Object;
}
private readonly ISqlSyntaxProvider _syntaxProvider = new SqlCeSyntaxProvider();
private readonly BaseMapper _contentMapper;
private readonly CachedExpression _cachedExpression;
private readonly MappingResolver _mappingResolver;
[Benchmark(Baseline = true)]
public void WithNonCached()
{
@@ -66,7 +62,7 @@ namespace Umbraco.Tests.Benchmarks
Expression<Func<IContent, bool>> predicate = content =>
content.Path.StartsWith("-1") && content.Published && (content.ContentTypeId == a || content.ContentTypeId == b);
var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor<IContent>(_syntaxProvider, _mappingResolver);
var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor<IContent>(_syntaxProvider, _contentMapper);
var result = modelToSqlExpressionHelper.Visit(predicate);
}
@@ -84,7 +80,7 @@ namespace Umbraco.Tests.Benchmarks
Expression<Func<IContent, bool>> predicate = content =>
content.Path.StartsWith("-1") && content.Published && (content.ContentTypeId == a || content.ContentTypeId == b);
var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor<IContent>(_syntaxProvider, _mappingResolver);
var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor<IContent>(_syntaxProvider, _contentMapper);
//wrap it!
_cachedExpression.Wrap(predicate);
+1 -4
View File
@@ -13,10 +13,7 @@ namespace Umbraco.Tests.Benchmarks
typeof(BulkInsertBenchmarks),
typeof(ModelToSqlExpressionHelperBenchmarks),
typeof(XmlBenchmarks),
typeof(LinqCastBenchmarks),
//typeof(DeepCloneBenchmarks),
typeof(XmlPublishedContentInitBenchmarks),
typeof(LinqCastBenchmarks)
});
switcher.Run(args);
}
@@ -49,9 +49,6 @@
<Reference Include="BenchmarkDotNet.Toolchains.Roslyn, Version=0.9.9.0, Culture=neutral, PublicKeyToken=aa0ca2f9092cefc4, processorArchitecture=MSIL">
<HintPath>..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.9.9\lib\net45\BenchmarkDotNet.Toolchains.Roslyn.dll</HintPath>
</Reference>
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.4.0.0\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CodeAnalysis, Version=1.3.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.CodeAnalysis.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.dll</HintPath>
</Reference>
@@ -61,9 +58,6 @@
<Reference Include="Microsoft.Diagnostics.Tracing.TraceEvent, Version=1.0.41.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.1.0.41\lib\net40\Microsoft.Diagnostics.Tracing.TraceEvent.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.7.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.7.0\lib\net45\Moq.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Collections.Immutable, Version=1.1.37.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.1.1.37\lib\dotnet\System.Collections.Immutable.dll</HintPath>
@@ -97,7 +91,6 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="XmlBenchmarks.cs" />
<Compile Include="XmlPublishedContentInitBenchmarks.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
@@ -112,10 +105,6 @@
<Project>{5d3b8245-ada6-453f-a008-50ed04bfe770}</Project>
<Name>Umbraco.Tests</Name>
</ProjectReference>
<ProjectReference Include="..\Umbraco.Web\Umbraco.Web.csproj">
<Project>{651e1350-91b6-44b7-bd60-7207006d7003}</Project>
<Name>Umbraco.Web</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
@@ -1,315 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnostics.Windows;
using BenchmarkDotNet.Jobs;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.PublishedCache.XmlPublishedCache;
namespace Umbraco.Tests.Benchmarks
{
[Config(typeof(Config))]
public class XmlPublishedContentInitBenchmarks
{
private class Config : ManualConfig
{
public Config()
{
Add(new MemoryDiagnoser());
//The 'quick and dirty' settings, so it runs a little quicker
// see benchmarkdotnet FAQ
Add(Job.Default
.WithLaunchCount(1) // benchmark process will be launched only once
.WithIterationTime(100) // 100ms per iteration
.WithWarmupCount(3) // 3 warmup iteration
.WithTargetCount(3)); // 3 target iteration
}
}
public XmlPublishedContentInitBenchmarks()
{
_xml10 = Build(10);
_xml100 = Build(100);
_xml1000 = Build(1000);
_xml10000 = Build(10000);
}
private readonly string[] _intAttributes = { "id", "parentID", "nodeType", "level", "writerID", "creatorID", "template", "sortOrder", "isDoc", "isDraft" };
private readonly string[] _strAttributes = { "nodeName", "urlName", "writerName", "creatorName", "path" };
private readonly string[] _dateAttributes = { "createDate", "updateDate" };
private readonly string[] _guidAttributes = { "key", "version" };
private XmlDocument Build(int children)
{
var xml = new XmlDocument();
var root = Build(xml, "Home", 10);
for (int i = 0; i < children; i++)
{
var child = Build(xml, "child" + i, 10);
root.AppendChild(child);
}
xml.AppendChild(root);
return xml;
}
private XmlElement Build(XmlDocument xml, string name, int propertyCount)
{
var random = new Random();
var content = xml.CreateElement(name);
foreach (var p in _intAttributes)
{
var a = xml.CreateAttribute(p);
a.Value = random.Next(1, 9).ToInvariantString();
content.Attributes.Append(a);
}
foreach (var p in _strAttributes)
{
var a = xml.CreateAttribute(p);
a.Value = Guid.NewGuid().ToString();
content.Attributes.Append(a);
}
foreach (var p in _guidAttributes)
{
var a = xml.CreateAttribute(p);
a.Value = Guid.NewGuid().ToString();
content.Attributes.Append(a);
}
foreach (var p in _dateAttributes)
{
var a = xml.CreateAttribute(p);
a.Value = DateTime.Now.ToString("o");
content.Attributes.Append(a);
}
for (int i = 0; i < propertyCount; i++)
{
var prop = xml.CreateElement("prop" + i);
var cdata = xml.CreateCDataSection(string.Join("", Enumerable.Range(0, 10).Select(x => Guid.NewGuid().ToString())));
prop.AppendChild(cdata);
content.AppendChild(prop);
}
return content;
}
private readonly XmlDocument _xml10;
private readonly XmlDocument _xml100;
private readonly XmlDocument _xml1000;
private readonly XmlDocument _xml10000;
//out props
int id, nodeType, level, writerId, creatorId, template, sortOrder;
Guid key, version;
string name, urlName, writerName, creatorName, docTypeAlias, path;
bool isDraft;
DateTime createDate, updateDate;
PublishedContentType publishedContentType;
Dictionary<string, IPublishedProperty> properties;
[Benchmark(Baseline = true, OperationsPerInvoke = 10)]
public void Original_10_Children()
{
OriginalInitializeNode(_xml10.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Original_100_Children()
{
OriginalInitializeNode(_xml100.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Original_1000_Children()
{
OriginalInitializeNode(_xml1000.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Original_10000_Children()
{
OriginalInitializeNode(_xml10000.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Enhanced_10_Children()
{
XmlPublishedContent.InitializeNode(_xml10.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties, GetPublishedContentType);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Enhanced_100_Children()
{
XmlPublishedContent.InitializeNode(_xml100.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties, GetPublishedContentType);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Enhanced_1000_Children()
{
XmlPublishedContent.InitializeNode(_xml1000.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties, GetPublishedContentType);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Enhanced_10000_Children()
{
XmlPublishedContent.InitializeNode(_xml10000.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties, GetPublishedContentType);
}
internal static void OriginalInitializeNode(XmlNode xmlNode, bool legacy, bool isPreviewing,
out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName,
out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path,
out Guid version, out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft,
out PublishedContentType contentType, out Dictionary<string, IPublishedProperty> properties)
{
//initialize the out params with defaults:
writerName = null;
docTypeAlias = null;
id = template = sortOrder = template = creatorId = writerId = docTypeId = level = default(int);
key = version = default(Guid);
name = writerName = urlName = creatorName = docTypeAlias = path = null;
createDate = updateDate = default(DateTime);
isDraft = false;
contentType = null;
properties = null;
//return if this is null
if (xmlNode == null)
{
return;
}
if (xmlNode.Attributes != null)
{
id = int.Parse(xmlNode.Attributes.GetNamedItem("id").Value);
if (xmlNode.Attributes.GetNamedItem("key") != null) // because, migration
key = Guid.Parse(xmlNode.Attributes.GetNamedItem("key").Value);
if (xmlNode.Attributes.GetNamedItem("template") != null)
template = int.Parse(xmlNode.Attributes.GetNamedItem("template").Value);
if (xmlNode.Attributes.GetNamedItem("sortOrder") != null)
sortOrder = int.Parse(xmlNode.Attributes.GetNamedItem("sortOrder").Value);
if (xmlNode.Attributes.GetNamedItem("nodeName") != null)
name = xmlNode.Attributes.GetNamedItem("nodeName").Value;
if (xmlNode.Attributes.GetNamedItem("writerName") != null)
writerName = xmlNode.Attributes.GetNamedItem("writerName").Value;
if (xmlNode.Attributes.GetNamedItem("urlName") != null)
urlName = xmlNode.Attributes.GetNamedItem("urlName").Value;
// Creatorname is new in 2.1, so published xml might not have it!
try
{
creatorName = xmlNode.Attributes.GetNamedItem("creatorName").Value;
}
catch
{
creatorName = writerName;
}
//Added the actual userID, as a user cannot be looked up via full name only...
if (xmlNode.Attributes.GetNamedItem("creatorID") != null)
creatorId = int.Parse(xmlNode.Attributes.GetNamedItem("creatorID").Value);
if (xmlNode.Attributes.GetNamedItem("writerID") != null)
writerId = int.Parse(xmlNode.Attributes.GetNamedItem("writerID").Value);
if (legacy)
{
if (xmlNode.Attributes.GetNamedItem("nodeTypeAlias") != null)
docTypeAlias = xmlNode.Attributes.GetNamedItem("nodeTypeAlias").Value;
}
else
{
docTypeAlias = xmlNode.Name;
}
if (xmlNode.Attributes.GetNamedItem("nodeType") != null)
docTypeId = int.Parse(xmlNode.Attributes.GetNamedItem("nodeType").Value);
if (xmlNode.Attributes.GetNamedItem("path") != null)
path = xmlNode.Attributes.GetNamedItem("path").Value;
if (xmlNode.Attributes.GetNamedItem("version") != null)
version = new Guid(xmlNode.Attributes.GetNamedItem("version").Value);
if (xmlNode.Attributes.GetNamedItem("createDate") != null)
createDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("createDate").Value);
if (xmlNode.Attributes.GetNamedItem("updateDate") != null)
updateDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("updateDate").Value);
if (xmlNode.Attributes.GetNamedItem("level") != null)
level = int.Parse(xmlNode.Attributes.GetNamedItem("level").Value);
isDraft = (xmlNode.Attributes.GetNamedItem("isDraft") != null);
}
// load data
var dataXPath = legacy ? "data" : "* [not(@isDoc)]";
var nodes = xmlNode.SelectNodes(dataXPath);
contentType = GetPublishedContentType(PublishedItemType.Content, docTypeAlias);
var propertyNodes = new Dictionary<string, XmlNode>();
if (nodes != null)
foreach (XmlNode n in nodes)
{
var attrs = n.Attributes;
if (attrs == null) continue;
var alias = legacy
? attrs.GetNamedItem("alias").Value
: n.Name;
propertyNodes[alias.ToLowerInvariant()] = n;
}
properties = contentType.PropertyTypes.Select(p =>
{
XmlNode n;
return propertyNodes.TryGetValue(p.PropertyTypeAlias.ToLowerInvariant(), out n)
? new XmlPublishedProperty(p, isPreviewing, n)
: new XmlPublishedProperty(p, isPreviewing);
}).Cast<IPublishedProperty>().ToDictionary(
x => x.PropertyTypeAlias,
x => x,
StringComparer.OrdinalIgnoreCase);
}
private static PublishedContentType GetPublishedContentType(PublishedItemType type, string alias)
{
return new PublishedContentType(alias, new string[] {},
new List<PublishedPropertyType>(Enumerable.Range(0, 10).Select(x => new PublishedPropertyType("prop" + x, 0, "test", initConverters:false))));
}
}
}
@@ -4,13 +4,11 @@
<package id="BenchmarkDotNet.Core" version="0.9.9" targetFramework="net45" />
<package id="BenchmarkDotNet.Diagnostics.Windows" version="0.9.9" targetFramework="net45" />
<package id="BenchmarkDotNet.Toolchains.Roslyn" version="0.9.9" targetFramework="net45" />
<package id="Castle.Core" version="4.0.0" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Common" version="1.3.2" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.CSharp" version="1.3.2" targetFramework="net45" />
<package id="Microsoft.Diagnostics.Tracing.TraceEvent" version="1.0.41" targetFramework="net45" />
<package id="Microsoft.SqlServer.Compact" version="4.0.8854.1" targetFramework="net45" />
<package id="Moq" version="4.7.0" targetFramework="net45" />
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
<package id="System.Collections" version="4.0.0" targetFramework="net45" />
<package id="System.Collections.Immutable" version="1.1.37" targetFramework="net45" />
-190
View File
@@ -1,190 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using NUnit.Framework;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Dependencies
{
[TestFixture]
public class NuGet
{
[Test]
public void NuGet_Package_Versions_Are_The_Same_In_All_Package_Config_Files()
{
var packagesAndVersions = GetNuGetPackagesInSolution();
Assert.IsTrue(packagesAndVersions.Any());
var failTest = false;
foreach (var package in packagesAndVersions.OrderBy(x => x.ConfigFilePath))
{
var matchingPackages = packagesAndVersions.Where(x => string.Equals(x.PackageName, package.PackageName, StringComparison.InvariantCultureIgnoreCase));
foreach (var matchingPackage in matchingPackages)
{
if (package.PackageVersion == matchingPackage.PackageVersion)
continue;
Debug.WriteLine("Package '{0}' with version {1} in {2} doesn't match with version {3} in {4}",
package.PackageName, package.PackageVersion, package.ConfigFilePath,
matchingPackage.PackageVersion, matchingPackage.ConfigFilePath);
failTest = true;
}
}
Assert.IsFalse(failTest);
}
[Test]
public void NuSpec_File_Matches_Installed_Dependencies()
{
var solutionDirectory = GetSolutionDirectory();
Assert.NotNull(solutionDirectory);
Assert.NotNull(solutionDirectory.Parent);
var nuSpecPath = solutionDirectory.Parent.FullName + "\\build\\NuSpecs\\";
Assert.IsTrue(Directory.Exists(nuSpecPath));
var packagesAndVersions = GetNuGetPackagesInSolution();
var failTest = false;
var nuSpecFiles = Directory.GetFiles(nuSpecPath);
foreach (var fileName in nuSpecFiles.Where(x => x.EndsWith("nuspec")))
{
var serializer = new XmlSerializer(typeof(NuSpec));
using (var reader = new StreamReader(fileName))
{
var nuspec = (NuSpec)serializer.Deserialize(reader);
var dependencies = nuspec.MetaData.dependencies;
//UmbracoCms.Core has version "[$version$]" - ignore
foreach (var dependency in dependencies.Where(x => x.Id != "UmbracoCms.Core"))
{
var dependencyVersionRange = dependency.Version.Replace("[", string.Empty).Replace("(", string.Empty).Split(',');
var dependencyMinimumVersion = dependencyVersionRange.First().Trim();
var matchingPackages = packagesAndVersions.Where(x => string.Equals(x.PackageName, dependency.Id,
StringComparison.InvariantCultureIgnoreCase)).ToList();
if (matchingPackages.Any() == false)
continue;
// NuGet_Package_Versions_Are_The_Same_In_All_Package_Config_Files test
// guarantees that all packages have one version, solutionwide, so it's okay to take First() here
if (dependencyMinimumVersion != matchingPackages.First().PackageVersion)
{
Debug.WriteLine("NuSpec dependency '{0}' with minimum version {1} in doesn't match with version {2} in the solution",
dependency.Id, dependencyMinimumVersion, matchingPackages.First().PackageVersion);
failTest = true;
}
}
}
}
Assert.IsFalse(failTest);
}
private List<PackageVersionMatcher> GetNuGetPackagesInSolution()
{
var packagesAndVersions = new List<PackageVersionMatcher>();
var solutionDirectory = GetSolutionDirectory();
if (solutionDirectory == null)
return packagesAndVersions;
var packageConfigFiles = new List<FileInfo>();
var packageDirectories =
solutionDirectory.GetDirectories().Where(x =>
x.Name.StartsWith("Umbraco.Tests") == false &&
x.Name.StartsWith("Umbraco.MSBuild.Tasks") == false).ToArray();
foreach (var directory in packageDirectories)
{
var packagesFiles = directory.EnumerateFiles("packages.config");
packageConfigFiles.AddRange(packagesFiles);
}
foreach (var file in packageConfigFiles)
{
//read all and de-duplicate packages
var serializer = new XmlSerializer(typeof(PackageConfigEntries));
using (var reader = new StreamReader(file.FullName))
{
var packages = (PackageConfigEntries)serializer.Deserialize(reader);
packagesAndVersions.AddRange(packages.Package.Select(package =>
new PackageVersionMatcher
{
PackageName = package.Id,
PackageVersion = package.Version,
ConfigFilePath = file.FullName
}));
}
}
return packagesAndVersions;
}
private DirectoryInfo GetSolutionDirectory()
{
var testsDirectory = new FileInfo(TestHelper.MapPathForTest("~/"));
if (testsDirectory.Directory == null)
return null;
if (testsDirectory.Directory.Parent == null || testsDirectory.Directory.Parent.Parent == null)
return null;
var solutionDirectory = testsDirectory.Directory.Parent.Parent.Parent;
return solutionDirectory;
}
}
public class PackageVersionMatcher
{
public string ConfigFilePath { get; set; }
public string PackageName { get; set; }
public string PackageVersion { get; set; }
}
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false, ElementName = "packages")]
public class PackageConfigEntries
{
[XmlElement("package")]
public PackagesPackage[] Package { get; set; }
}
[XmlType(AnonymousType = true, TypeName = "package")]
public class PackagesPackage
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
}
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd")]
[XmlRoot(Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", IsNullable = false, ElementName = "package")]
public class NuSpec
{
[XmlElement("metadata")]
public Metadata MetaData { get; set; }
}
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", TypeName = "metadata")]
public class Metadata
{
[XmlArrayItem("dependency", IsNullable = false)]
//TODO: breaks when renamed to capital D
public Dependency[] dependencies { get; set; }
}
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", TypeName = "dependencies")]
public class Dependency
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
}
}
-92
View File
@@ -1,92 +0,0 @@
using System;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Issues
{
[TestFixture]
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
public class U9560 : BaseDatabaseFactoryTest
{
[Test]
public void Test()
{
// create a content type and some properties
var contentType = new ContentType(-1);
contentType.Alias = "test";
contentType.Name = "test";
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext, "prop") { Name = "Prop", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 };
contentType.PropertyTypeCollection.Add(propertyType);
ServiceContext.ContentTypeService.Save(contentType);
var aliasName = string.Empty;
// read fields, same as what we do with PetaPoco Fetch<dynamic>
var db = DatabaseContext.Database;
db.OpenSharedConnection();
try
{
var conn = db.Connection;
var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT mandatory, dataTypeId, propertyTypeGroupId, contentTypeId, sortOrder, alias, name, validationRegExp, description from cmsPropertyType where id=" + propertyType.Id;
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
for (var i = 0; i < reader.FieldCount; i++)
Console.WriteLine(reader.GetName(i));
aliasName = reader.GetName(5);
}
}
}
finally
{
db.CloseSharedConnection();
}
// note that although the query is for 'alias' the field is named 'Alias'
Assert.AreEqual("Alias", aliasName);
// try differently
db.OpenSharedConnection();
try
{
var conn = db.Connection;
var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT mandatory, dataTypeId, propertyTypeGroupId, contentTypeId, sortOrder, alias as alias, name, validationRegExp, description from cmsPropertyType where id=" + propertyType.Id;
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
for (var i = 0; i < reader.FieldCount; i++)
Console.WriteLine(reader.GetName(i));
aliasName = reader.GetName(5);
}
}
}
finally
{
db.CloseSharedConnection();
}
// and now it is OK
Assert.AreEqual("alias", aliasName);
// get the legacy content type
var legacyContentType = new umbraco.cms.businesslogic.ContentType(contentType.Id);
Assert.AreEqual("test", legacyContentType.Alias);
// get the legacy properties
var legacyProperties = legacyContentType.PropertyTypes;
// without the fix, due to some (swallowed) inner exception, we have no properties
//Assert.IsNull(legacyProperties);
// thanks to the fix, it works
Assert.IsNotNull(legacyProperties);
Assert.AreEqual(1, legacyProperties.Count);
Assert.AreEqual("prop", legacyProperties[0].Alias);
}
}
}
@@ -34,7 +34,7 @@ namespace Umbraco.Tests.Persistence
{
DatabaseContext = _dbContext,
IsReady = true
};
};
}
[TearDown]
@@ -44,13 +44,6 @@ namespace Umbraco.Tests.Persistence
ApplicationContext.Current = null;
}
[Test]
public void Database_Connection()
{
var db = _dbContext.Database;
Assert.IsNull(db.Connection);
}
[Test]
public void Can_Verify_Single_Database_Instance()
{
@@ -106,7 +99,7 @@ namespace Umbraco.Tests.Persistence
var appCtx = new ApplicationContext(
new DatabaseContext(Mock.Of<IDatabaseFactory>(), Mock.Of<ILogger>(), Mock.Of<ISqlSyntaxProvider>(), "test"),
new ServiceContext(migrationEntryService: Mock.Of<IMigrationEntryService>()),
new ServiceContext(migrationEntryService: Mock.Of<IMigrationEntryService>()),
CacheHelper.CreateDisabledCacheHelper(),
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
@@ -1,6 +1,5 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using Moq;
using NUnit.Framework;
@@ -17,52 +16,19 @@ namespace Umbraco.Tests.Persistence.Querying
[TestFixture]
public class ExpressionTests : BaseUsingSqlCeSyntax
{
[Test]
public void Equals_Claus_With_Two_Entity_Values()
{
var dataType = new DataTypeDefinition(-1, "Test")
{
Id = 12345
};
Expression<Func<PropertyType, bool>> predicate = p => p.DataTypeDefinitionId == dataType.Id;
var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor<PropertyType>();
var result = modelToSqlExpressionHelper.Visit(predicate);
// [Test]
// public void Can_Query_With_Content_Type_Alias()
// {
// //Arrange
// Expression<Func<IMedia, bool>> predicate = content => content.ContentType.Alias == "Test";
// var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor<IContent>();
// var result = modelToSqlExpressionHelper.Visit(predicate);
Debug.Print("Model to Sql ExpressionHelper: \n" + result);
// Debug.Print("Model to Sql ExpressionHelper: \n" + result);
Assert.AreEqual("([cmsPropertyType].[dataTypeId] = @0)", result);
Assert.AreEqual(12345, modelToSqlExpressionHelper.GetSqlParameters()[0]);
}
[Test]
public void Can_Query_With_Content_Type_Alias()
{
//Arrange
Expression<Func<IMedia, bool>> predicate = content => content.ContentType.Alias == "Test";
var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor<IContent>();
var result = modelToSqlExpressionHelper.Visit(predicate);
Debug.Print("Model to Sql ExpressionHelper: \n" + result);
Assert.AreEqual("([cmsContentType].[alias] = @0)", result);
Assert.AreEqual("Test", modelToSqlExpressionHelper.GetSqlParameters()[0]);
}
[Test]
public void Can_Query_With_Content_Type_Aliases()
{
//Arrange
var aliases = new[] {"Test1", "Test2"};
Expression<Func<IMedia, bool>> predicate = content => aliases.Contains(content.ContentType.Alias);
var modelToSqlExpressionHelper = new ModelToSqlExpressionVisitor<IContent>();
var result = modelToSqlExpressionHelper.Visit(predicate);
Debug.Print("Model to Sql ExpressionHelper: \n" + result);
Assert.AreEqual("[cmsContentType].[alias] IN (@1,@2)", result);
Assert.AreEqual("Test1", modelToSqlExpressionHelper.GetSqlParameters()[1]);
Assert.AreEqual("Test2", modelToSqlExpressionHelper.GetSqlParameters()[2]);
}
// Assert.AreEqual("[cmsContentType].[alias] = @0", result);
// Assert.AreEqual("Test", modelToSqlExpressionHelper.GetSqlParameters()[0]);
// }
[Test]
public void CachedExpression_Can_Verify_Path_StartsWith_Predicate_In_Same_Result()
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
@@ -22,6 +21,7 @@ using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
using umbraco.editorControls.tinyMCE3;
using umbraco.interfaces;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Tests.Persistence.Repositories
@@ -36,15 +36,11 @@ namespace Umbraco.Tests.Persistence.Repositories
base.Initialize();
CreateTestData();
VersionableRepositoryBase<int, IContent>.ThrowOnWarning = true;
}
[TearDown]
public override void TearDown()
{
VersionableRepositoryBase<int, IContent>.ThrowOnWarning = false;
base.TearDown();
}
@@ -71,191 +67,6 @@ namespace Umbraco.Tests.Persistence.Repositories
return repository;
}
[Test]
public void Get_Always_Returns_Latest_Version()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentTypeRepository contentTypeRepository;
IContent content1;
var versions = new List<Guid>();
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage");
content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType);
//save version
contentTypeRepository.AddOrUpdate(hasPropertiesContentType);
repository.AddOrUpdate(content1);
unitOfWork.Commit();
versions.Add(content1.Version);
//publish version
content1.ChangePublishedState(PublishedState.Published);
repository.AddOrUpdate(content1);
unitOfWork.Commit();
versions.Add(content1.Version);
//change something and make a pending version
content1.Name = "new name";
content1.ChangePublishedState(PublishedState.Saved);
repository.AddOrUpdate(content1);
unitOfWork.Commit();
versions.Add(content1.Version);
}
// Assert
Assert.AreEqual(3, versions.Distinct().Count());
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
var content = repository.GetByQuery(new Query<IContent>().Where(c => c.Id == content1.Id)).ToArray()[0];
Assert.AreEqual(versions[2], content.Version);
content = repository.Get(content1.Id);
Assert.AreEqual(versions[2], content.Version);
foreach (var version in versions)
{
content = repository.GetByVersion(version);
Assert.IsNotNull(content);
Assert.AreEqual(version, content.Version);
}
}
}
[Test]
public void Deal_With_Corrupt_Duplicate_Newest_Published_Flags()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentTypeRepository contentTypeRepository;
IContent content1;
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage");
content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType);
contentTypeRepository.AddOrUpdate(hasPropertiesContentType);
repository.AddOrUpdate(content1);
unitOfWork.Commit();
}
var versionDtos = new List<ContentVersionDto>();
//Now manually corrupt the data
var versions = new[] { Guid.NewGuid(), Guid.NewGuid() };
for (var index = 0; index < versions.Length; index++)
{
var version = versions[index];
var versionDate = DateTime.Now.AddMinutes(index);
var versionDto = new ContentVersionDto
{
NodeId = content1.Id,
VersionDate = versionDate,
VersionId = version
};
this.DatabaseContext.Database.Insert(versionDto);
versionDtos.Add(versionDto);
this.DatabaseContext.Database.Insert(new DocumentDto
{
Newest = true,
NodeId = content1.Id,
Published = true,
Text = content1.Name,
VersionId = version,
WriterUserId = 0,
UpdateDate = versionDate,
TemplateId = content1.Template == null || content1.Template.Id <= 0 ? null : (int?) content1.Template.Id
});
}
// Assert
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
var content = repository.GetByQuery(new Query<IContent>().Where(c => c.Id == content1.Id)).ToArray();
Assert.AreEqual(1, content.Length);
Assert.AreEqual(content[0].Version, versionDtos.Single(x => x.Id == versionDtos.Max(y => y.Id)).VersionId);
Assert.AreEqual(content[0].UpdateDate.ToString(CultureInfo.InvariantCulture), versionDtos.Single(x => x.Id == versionDtos.Max(y => y.Id)).VersionDate.ToString(CultureInfo.InvariantCulture));
var contentItem = repository.GetByVersion(content1.Version);
Assert.IsNotNull(contentItem);
contentItem = repository.Get(content1.Id);
Assert.IsNotNull(contentItem);
Assert.AreEqual(contentItem.UpdateDate.ToString(CultureInfo.InvariantCulture), versionDtos.Single(x => x.Id == versionDtos.Max(y => y.Id)).VersionDate.ToString(CultureInfo.InvariantCulture));
Assert.AreEqual(contentItem.Version, versionDtos.Single(x => x.Id == versionDtos.Max(y => y.Id)).VersionId);
var allVersions = repository.GetAllVersions(content[0].Id);
var allKnownVersions = versionDtos.Select(x => x.VersionId).Union(new[]{ content1.Version }).ToArray();
Assert.IsTrue(allKnownVersions.ContainsAll(allVersions.Select(x => x.Version)));
Assert.IsTrue(allVersions.Select(x => x.Version).ContainsAll(allKnownVersions));
}
}
/// <summary>
/// This tests the regression issue of U4-9438
/// </summary>
/// <remarks>
/// The problem was the iteration of the property data in VersionableRepositoryBase when a content item
/// in the list actually doesn't have any property types, it would still skip over a property row.
/// To test, we have 3 content items, the first has properties, the second doesn't and the third does.
/// </remarks>
[Test]
public void Property_Data_Assigned_Correctly()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentTypeRepository contentTypeRepository;
var allContent = new List<IContent>();
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
var emptyContentType = MockedContentTypes.CreateBasicContentType();
var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage");
var content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType);
var content2 = MockedContent.CreateBasicContent(emptyContentType);
var content3 = MockedContent.CreateSimpleContent(hasPropertiesContentType);
// Act
contentTypeRepository.AddOrUpdate(emptyContentType);
contentTypeRepository.AddOrUpdate(hasPropertiesContentType);
repository.AddOrUpdate(content1);
repository.AddOrUpdate(content2);
repository.AddOrUpdate(content3);
unitOfWork.Commit();
allContent.Add(content1);
allContent.Add(content2);
allContent.Add(content3);
}
// Assert
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
//this will cause the GetPropertyCollection to execute and we need to ensure that
// all of the properties and property types are all correct
var result = repository.GetAll(allContent.Select(x => x.Id).ToArray()).ToArray();
foreach (var content in result)
{
foreach (var contentProperty in content.Properties)
{
//prior to the fix, the 2nd document iteration in the GetPropertyCollection would have caused
//the enumerator to move forward past the first property of the 3rd document which would have
//ended up not assiging a property to the 3rd document. This would have ended up with the 3rd
//document still having 3 properties but the last one would not have been assigned an identity
//because the property data would not have been assigned.
Assert.IsTrue(contentProperty.HasIdentity);
}
}
}
}
[Test]
public void Rebuild_Xml_Structures_With_Non_Latest_Version()
{
@@ -355,62 +166,6 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Rebuild_All_Xml_Structures_Ensure_Orphaned_Are_Removed()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentTypeRepository contentTypeRepository;
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
//delete all xml
unitOfWork.Database.Execute("DELETE FROM cmsContentXml");
var contentType1 = MockedContentTypes.CreateSimpleContentType("Textpage1", "Textpage1");
contentTypeRepository.AddOrUpdate(contentType1);
var allCreated = new List<IContent>();
for (var i = 0; i < 100; i++)
{
//These will be non-published so shouldn't show up
var c1 = MockedContent.CreateSimpleContent(contentType1);
repository.AddOrUpdate(c1);
allCreated.Add(c1);
}
for (var i = 0; i < 100; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType1);
c1.ChangePublishedState(PublishedState.Published);
repository.AddOrUpdate(c1);
allCreated.Add(c1);
}
unitOfWork.Commit();
//now create some versions of this content - this shouldn't affect the xml structures saved
for (int i = 0; i < allCreated.Count; i++)
{
allCreated[i].Name = "blah" + i;
repository.AddOrUpdate(allCreated[i]);
}
unitOfWork.Commit();
//Add some extra orphaned rows that shouldn't be there
var notPublished = MockedContent.CreateSimpleContent(contentType1);
repository.AddOrUpdate(notPublished);
unitOfWork.Commit();
//Force add it
unitOfWork.Database.Insert(new ContentXmlDto
{
NodeId = notPublished.Id,
Xml = "<test></test>"
});
repository.RebuildXmlStructures(media => new XElement("test"), 10);
Assert.AreEqual(100, unitOfWork.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM cmsContentXml"));
}
}
[Test]
public void Rebuild_All_Xml_Structures_For_Content_Type()
{
@@ -172,38 +172,6 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Can_Get_All_Containers()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
EntityContainer container1, container2, container3;
using (var containerRepository = CreateContainerRepository(unitOfWork, Constants.ObjectTypes.DocumentTypeContainerGuid))
{
container1 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "container1" };
containerRepository.AddOrUpdate(container1);
container2 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "container2" };
containerRepository.AddOrUpdate(container2);
container3 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "container3" };
containerRepository.AddOrUpdate(container3);
unitOfWork.Commit();
Assert.That(container1.Id, Is.GreaterThan(0));
Assert.That(container2.Id, Is.GreaterThan(0));
Assert.That(container3.Id, Is.GreaterThan(0));
}
using (var containerRepository = CreateContainerRepository(unitOfWork, Constants.ObjectTypes.DocumentTypeContainerGuid))
{
var found1 = containerRepository.Get(container1.Id);
Assert.IsNotNull(found1);
var found2 = containerRepository.Get(container2.Id);
Assert.IsNotNull(found2);
var found3 = containerRepository.Get(container3.Id);
Assert.IsNotNull(found3);
var allContainers = containerRepository.GetAll();
Assert.AreEqual(3, allContainers.Count());
}
}
[Test]
public void Can_Delete_Container()
{
@@ -1,183 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Persistence.Repositories
{
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
[TestFixture]
public class EntityRepositoryTest : BaseDatabaseFactoryTest
{
[SetUp]
public override void Initialize()
{
base.Initialize();
}
[TearDown]
public override void TearDown()
{
base.TearDown();
}
private ContentRepository CreateContentRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository)
{
TemplateRepository tr;
return CreateContentRepository(unitOfWork, out contentTypeRepository, out tr);
}
private ContentRepository CreateContentRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out TemplateRepository templateRepository)
{
templateRepository = new TemplateRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>());
var tagRepository = new TagRepository(unitOfWork, CacheHelper, Logger, SqlSyntax);
contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, templateRepository);
var repository = new ContentRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, contentTypeRepository, templateRepository, tagRepository, Mock.Of<IContentSection>());
return repository;
}
[Test]
public void Deal_With_Corrupt_Duplicate_Newest_Published_Flags_Full_Model()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentTypeRepository contentTypeRepository;
IContent content1;
using (var repository = CreateContentRepository(unitOfWork, out contentTypeRepository))
{
var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage");
content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType);
contentTypeRepository.AddOrUpdate(hasPropertiesContentType);
repository.AddOrUpdate(content1);
unitOfWork.Commit();
}
var versionDtos = new List<ContentVersionDto>();
//Now manually corrupt the data
var versions = new[] {Guid.NewGuid(), Guid.NewGuid()};
for (var index = 0; index < versions.Length; index++)
{
var version = versions[index];
var versionDate = DateTime.Now.AddMinutes(index);
var versionDto = new ContentVersionDto
{
NodeId = content1.Id,
VersionDate = versionDate,
VersionId = version
};
this.DatabaseContext.Database.Insert(versionDto);
versionDtos.Add(versionDto);
this.DatabaseContext.Database.Insert(new DocumentDto
{
Newest = true,
NodeId = content1.Id,
Published = true,
Text = content1.Name,
VersionId = version,
WriterUserId = 0,
UpdateDate = versionDate,
TemplateId = content1.Template == null || content1.Template.Id <= 0 ? null : (int?)content1.Template.Id
});
}
// Assert
using (var repository = new EntityRepository(unitOfWork))
{
var content = repository.GetByQuery(new Query<IUmbracoEntity>().Where(c => c.Id == content1.Id), Constants.ObjectTypes.DocumentGuid).ToArray();
Assert.AreEqual(1, content.Length);
Assert.AreEqual(versionDtos.Max(x => x.Id), content[0].AdditionalData["VersionId"]);
content = repository.GetAll(Constants.ObjectTypes.DocumentGuid, content[0].Key).ToArray();
Assert.AreEqual(1, content.Length);
Assert.AreEqual(versionDtos.Max(x => x.Id), content[0].AdditionalData["VersionId"]);
content = repository.GetAll(Constants.ObjectTypes.DocumentGuid, content[0].Id).ToArray();
Assert.AreEqual(1, content.Length);
Assert.AreEqual(versionDtos.Max(x => x.Id), content[0].AdditionalData["VersionId"]);
var contentItem = repository.Get(content[0].Id, Constants.ObjectTypes.DocumentGuid);
Assert.AreEqual(versionDtos.Max(x => x.Id), contentItem.AdditionalData["VersionId"]);
contentItem = repository.GetByKey(content[0].Key, Constants.ObjectTypes.DocumentGuid);
Assert.AreEqual(versionDtos.Max(x => x.Id), contentItem.AdditionalData["VersionId"]);
}
}
/// <summary>
/// The Slim model will test the EntityRepository when it doesn't know the object type so it will
/// make the simplest (slim) query
/// </summary>
[Test]
public void Deal_With_Corrupt_Duplicate_Newest_Published_Flags_Slim_Model()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentTypeRepository contentTypeRepository;
IContent content1;
using (var repository = CreateContentRepository(unitOfWork, out contentTypeRepository))
{
var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage");
content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType);
contentTypeRepository.AddOrUpdate(hasPropertiesContentType);
repository.AddOrUpdate(content1);
unitOfWork.Commit();
}
//Now manually corrupt the data
var versions = new[] { Guid.NewGuid(), Guid.NewGuid() };
for (var index = 0; index < versions.Length; index++)
{
var version = versions[index];
var versionDate = DateTime.Now.AddMinutes(index);
var versionDto = new ContentVersionDto
{
NodeId = content1.Id,
VersionDate = versionDate,
VersionId = version
};
this.DatabaseContext.Database.Insert(versionDto);
this.DatabaseContext.Database.Insert(new DocumentDto
{
Newest = true,
NodeId = content1.Id,
Published = true,
Text = content1.Name,
VersionId = version,
WriterUserId = 0,
UpdateDate = versionDate,
TemplateId = content1.Template == null || content1.Template.Id <= 0 ? null : (int?)content1.Template.Id
});
}
// Assert
using (var repository = new EntityRepository(unitOfWork))
{
var content = repository.GetByQuery(new Query<IUmbracoEntity>().Where(c => c.Id == content1.Id)).ToArray();
Assert.AreEqual(1, content.Length);
var contentItem = repository.Get(content[0].Id);
Assert.IsNotNull(contentItem);
contentItem = repository.GetByKey(content[0].Key);
Assert.IsNotNull(contentItem);
}
}
}
}
@@ -4,7 +4,6 @@ using System.Linq;
using System.Xml.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
@@ -71,44 +70,6 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Rebuild_All_Xml_Structures_Ensure_Orphaned_Are_Removed()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
MediaTypeRepository mediaTypeRepository;
using (var repository = CreateRepository(unitOfWork, out mediaTypeRepository))
{
//delete all xml
unitOfWork.Database.Execute("DELETE FROM cmsContentXml");
var mediaType = mediaTypeRepository.Get(1032);
for (var i = 0; i < 100; i++)
{
var image = MockedMedia.CreateMediaImage(mediaType, -1);
repository.AddOrUpdate(image);
}
unitOfWork.Commit();
//Add some extra orphaned rows that shouldn't be there
var trashed = MockedMedia.CreateMediaImage(mediaType, -1);
trashed.ChangeTrashedState(true, Constants.System.RecycleBinMedia);
repository.AddOrUpdate(trashed);
unitOfWork.Commit();
//Force add it
unitOfWork.Database.Insert(new ContentXmlDto
{
NodeId = trashed.Id,
Xml = "<test></test>"
});
repository.RebuildXmlStructures(media => new XElement("test"), 10);
Assert.AreEqual(103, unitOfWork.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM cmsContentXml"));
}
}
[Test]
public void Rebuild_Some_Xml_Structures()
{
@@ -384,61 +345,6 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Can_Perform_GetByQuery_On_MediaRepository_With_ContentType_Id_Filter()
{
// Arrange
var folderMediaType = ServiceContext.ContentTypeService.GetMediaType(1031);
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
MediaTypeRepository mediaTypeRepository;
using (var repository = CreateRepository(unitOfWork, out mediaTypeRepository))
{
// Act
for (int i = 0; i < 10; i++)
{
var folder = MockedMedia.CreateMediaFolder(folderMediaType, -1);
repository.AddOrUpdate(folder);
}
unitOfWork.Commit();
var types = new[] { 1031 };
var query = Query<IMedia>.Builder.Where(x => types.Contains(x.ContentTypeId));
var result = repository.GetByQuery(query);
// Assert
Assert.That(result.Count(), Is.GreaterThanOrEqualTo(11));
}
}
[Ignore("We could allow this to work but it requires an extra join on the query used which currently we don't absolutely need so leaving this out for now")]
[Test]
public void Can_Perform_GetByQuery_On_MediaRepository_With_ContentType_Alias_Filter()
{
// Arrange
var folderMediaType = ServiceContext.ContentTypeService.GetMediaType(1031);
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
MediaTypeRepository mediaTypeRepository;
using (var repository = CreateRepository(unitOfWork, out mediaTypeRepository))
{
// Act
for (int i = 0; i < 10; i++)
{
var folder = MockedMedia.CreateMediaFolder(folderMediaType, -1);
repository.AddOrUpdate(folder);
}
unitOfWork.Commit();
var types = new[] { "Folder" };
var query = Query<IMedia>.Builder.Where(x => types.Contains(x.ContentType.Alias));
var result = repository.GetByQuery(query);
// Assert
Assert.That(result.Count(), Is.GreaterThanOrEqualTo(11));
}
}
[Test]
public void Can_Perform_GetPagedResultsByQuery_ForFirstPage_On_MediaRepository()
{
@@ -564,7 +470,7 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.That(result.First().Name, Is.EqualTo("Test File"));
}
}
[Test]
public void Can_Perform_GetPagedResultsByQuery_WithFilterMatchingAll_On_MediaRepository()
{
@@ -154,61 +154,36 @@ namespace Umbraco.Tests.Persistence.Repositories
[Test]
public void Get_All()
{
var content = CreateTestData(30).ToArray();
var content = CreateTestData(3).ToArray();
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
using (var repo = new PublicAccessRepository(unitOfWork, CacheHelper, Logger, SqlSyntax))
{
var allEntries = new List<PublicAccessEntry>();
for (int i = 0; i < 10; i++)
{
var entry1 = new PublicAccessEntry(content[0], content[1], content[2], new[]
{
var rules = new List<PublicAccessRule>();
for (int j = 0; j < 50; j++)
new PublicAccessRule
{
rules.Add(new PublicAccessRule
{
RuleValue = "test" + j,
RuleType = "RoleName" + j
});
}
var entry1 = new PublicAccessEntry(content[i], content[i+1], content[i+2], rules);
repo.AddOrUpdate(entry1);
unitOfWork.Commit();
allEntries.Add(entry1);
}
RuleValue = "test",
RuleType = "RoleName"
},
});
repo.AddOrUpdate(entry1);
//now remove a few rules from a few of the items and then add some more, this will put things 'out of order' which
//we need to verify our sort order is working for the relator
for (int i = 0; i < allEntries.Count; i++)
var entry2 = new PublicAccessEntry(content[1], content[0], content[2], new[]
{
//all the even ones
if (i % 2 == 0)
new PublicAccessRule
{
var rules = allEntries[i].Rules.ToArray();
for (int j = 0; j < rules.Length; j++)
{
//all the even ones
if (j % 2 == 0)
{
allEntries[i].RemoveRule(rules[j]);
}
}
allEntries[i].AddRule("newrule" + i, "newrule" + i);
repo.AddOrUpdate(allEntries[i]);
unitOfWork.Commit();
}
}
RuleValue = "test",
RuleType = "RoleName"
},
});
repo.AddOrUpdate(entry2);
unitOfWork.Commit();
var found = repo.GetAll().ToArray();
Assert.AreEqual(10, found.Length);
foreach (var publicAccessEntry in found)
{
var matched = allEntries.First(x => x.Key == publicAccessEntry.Key);
Assert.AreEqual(matched.Rules.Count(), publicAccessEntry.Rules.Count());
}
Assert.AreEqual(2, found.Count());
}
}
@@ -1,8 +1,6 @@
using System;
using System.Linq;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Store;
using Moq;
using NUnit.Framework;
@@ -33,15 +31,12 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Ensure_Children_Are_Sorted()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
using (var luceneDir = new RAMDirectory())
{
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile());
Assert.IsNotNull(result);
Assert.AreEqual(1, result.TotalItemCount);
@@ -65,15 +60,12 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Ensure_Result_Has_All_Values()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
using (var luceneDir = new RAMDirectory())
{
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile());
Assert.IsNotNull(result);
Assert.AreEqual(1, result.TotalItemCount);
@@ -27,7 +27,6 @@ using UmbracoExamine;
using UmbracoExamine.DataServices;
using umbraco.BusinessLogic;
using System.Linq;
using Lucene.Net.Index;
namespace Umbraco.Tests.PublishedContent
{
@@ -107,14 +106,11 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Ensure_Children_Sorted_With_Examine()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
using (var luceneDir = new RAMDirectory())
{
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -139,14 +135,11 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Do_Not_Find_In_Recycle_Bin()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
using (var luceneDir = new RAMDirectory())
{
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -182,14 +175,11 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Children_With_Examine()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
using (var luceneDir = new RAMDirectory())
{
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -207,14 +197,11 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Descendants_With_Examine()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
using (var luceneDir = new RAMDirectory())
{
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -232,14 +219,11 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void DescendantsOrSelf_With_Examine()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
using (var luceneDir = new RAMDirectory())
{
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -257,15 +241,12 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Ancestors_With_Examine()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
using (var luceneDir = new RAMDirectory())
{
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var ctx = GetUmbracoContext("/test", 1234);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
@@ -279,15 +260,12 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void AncestorsOrSelf_With_Examine()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
using (var luceneDir = new RAMDirectory())
{
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var ctx = GetUmbracoContext("/test", 1234);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
@@ -36,15 +36,11 @@ namespace Umbraco.Tests.Services
public override void Initialize()
{
base.Initialize();
VersionableRepositoryBase<int, IContent>.ThrowOnWarning = true;
}
[TearDown]
public override void TearDown()
{
VersionableRepositoryBase<int, IContent>.ThrowOnWarning = false;
base.TearDown();
}
@@ -672,17 +668,6 @@ namespace Umbraco.Tests.Services
Assert.Throws<Exception>(() => contentService.CreateContent("Test", -1, "umbAliasDoesntExist"));
}
[Test]
public void Cannot_Save_Content_With_Empty_Name()
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = new Content(string.Empty, -1, ServiceContext.ContentTypeService.GetContentType("umbTextpage"));
// Act & Assert
Assert.Throws<ArgumentException>(() => contentService.Save(content));
}
[Test]
public void Can_Get_Content_By_Id()
{
@@ -766,26 +751,16 @@ namespace Umbraco.Tests.Services
var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1);
ServiceContext.ContentService.Publish(parent);//Publishing root, so Text Page 2 can be updated.
var subpage2 = contentService.GetById(NodeDto.NodeIdSeed + 3);
subpage2.Name = "Text Page 2 Updated";
subpage2.SetValue("author", "Jane Doe");
contentService.SaveAndPublishWithStatus(subpage2, 0);//NOTE New versions are only added between publish-state-changed, so publishing to ensure addition version.
subpage2.Name = "Text Page 2 Updated again";
subpage2.SetValue("author", "Bob Hope");
contentService.SaveAndPublishWithStatus(subpage2, 0);//NOTE New versions are only added between publish-state-changed, so publishing to ensure addition version.
// Act
var versions = contentService.GetVersions(NodeDto.NodeIdSeed + 3).ToList();
// Assert
Assert.That(versions.Any(), Is.True);
Assert.That(versions.Count(), Is.GreaterThanOrEqualTo(2));
//ensure each version contains the correct property values
Assert.AreEqual("John Doe", versions[2].GetValue("author"));
Assert.AreEqual("Jane Doe", versions[1].GetValue("author"));
Assert.AreEqual("Bob Hope", versions[0].GetValue("author"));
}
[Test]
@@ -1353,69 +1328,6 @@ namespace Umbraco.Tests.Services
Assert.That(contents.Any(), Is.False);
}
[Test]
public void Can_Empty_RecycleBin_With_Content_That_Has_All_Related_Data()
{
// Arrange
//need to:
// * add relations
// * add permissions
// * add notifications
// * public access
// * tags
// * domain
// * published & preview data
// * multiple versions
var contentType = MockedContentTypes.CreateAllTypesContentType("test", "test");
ServiceContext.ContentTypeService.Save(contentType, 0);
object obj =
new
{
tags = "Hello,World"
};
var content1 = MockedContent.CreateBasicContent(contentType);
content1.PropertyValues(obj);
content1.ResetDirtyProperties(false);
ServiceContext.ContentService.Save(content1, 0);
Assert.IsTrue(ServiceContext.ContentService.PublishWithStatus(content1, 0).Success);
var content2 = MockedContent.CreateBasicContent(contentType);
content2.PropertyValues(obj);
content2.ResetDirtyProperties(false);
ServiceContext.ContentService.Save(content2, 0);
Assert.IsTrue(ServiceContext.ContentService.PublishWithStatus(content2, 0).Success);
ServiceContext.RelationService.Save(new RelationType(Constants.ObjectTypes.DocumentGuid, Constants.ObjectTypes.DocumentGuid, "test"));
Assert.IsNotNull(ServiceContext.RelationService.Relate(content1, content2, "test"));
ServiceContext.PublicAccessService.Save(new PublicAccessEntry(content1, content2, content2, new List<PublicAccessRule>
{
new PublicAccessRule
{
RuleType = "test",
RuleValue = "test"
}
}));
Assert.IsTrue(ServiceContext.PublicAccessService.AddRule(content1, "test2", "test2").Success);
Assert.IsNotNull(ServiceContext.NotificationService.CreateNotification(ServiceContext.UserService.GetUserById(0), content1, "test"));
ServiceContext.ContentService.AssignContentPermission(content1, 'A', new[] {0});
Assert.IsTrue(ServiceContext.DomainService.Save(new UmbracoDomain("www.test.com", "en-AU")
{
RootContentId = content1.Id
}).Success);
// Act
ServiceContext.ContentService.EmptyRecycleBin();
var contents = ServiceContext.ContentService.GetContentInRecycleBin();
// Assert
Assert.That(contents.Any(), Is.False);
}
[Test]
public void Can_Move_Content()
{
@@ -304,42 +304,6 @@ namespace Umbraco.Tests.Services
Assert.IsNull(deletedContentType);
}
[Test]
public void Can_Create_Container()
{
// Arrange
var cts = ServiceContext.ContentTypeService;
// Act
var container = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid);
container.Name = "container1";
cts.SaveContentTypeContainer(container);
// Assert
var createdContainer = cts.GetContentTypeContainer(container.Id);
Assert.IsNotNull(createdContainer);
}
[Test]
public void Can_Get_All_Containers()
{
// Arrange
var cts = ServiceContext.ContentTypeService;
// Act
var container1 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid);
container1.Name = "container1";
cts.SaveContentTypeContainer(container1);
var container2 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid);
container2.Name = "container2";
cts.SaveContentTypeContainer(container2);
// Assert
var containers = cts.GetContentTypeContainers(new int[0]);
Assert.AreEqual(2, containers.Count());
}
[Test]
public void Deleting_ContentType_Sends_Correct_Number_Of_DeletedEntities_In_Events()
{
@@ -753,16 +717,6 @@ namespace Umbraco.Tests.Services
Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
}
[Test]
public void Cannot_Save_ContentType_With_Empty_Name()
{
// Arrange
var contentType = MockedContentTypes.CreateSimpleContentType("contentType", string.Empty);
// Act & Assert
Assert.Throws<ArgumentException>(() => ServiceContext.ContentTypeService.Save(contentType));
}
[Test]
public void Cannot_Rename_PropertyType_Alias_On_Composition_Which_Would_Cause_Conflict_In_Other_Composition()
{
@@ -221,18 +221,5 @@ namespace Umbraco.Tests.Services
Assert.AreEqual("preVal1", preVals.PreValuesAsArray.First().Value);
Assert.AreEqual("preVal2", preVals.PreValuesAsArray.Last().Value);
}
[Test]
public void Cannot_Save_DataType_With_Empty_Name()
{
// Arrange
var dataTypeService = ServiceContext.DataTypeService;
// Act
var dataTypeDefinition = new DataTypeDefinition(-1, "Test.TestEditor") { Name = string.Empty, DatabaseType = DataTypeDatabaseType.Ntext };
// Act & Assert
Assert.Throws<ArgumentException>(() => dataTypeService.Save(dataTypeDefinition));
}
}
}
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
@@ -218,17 +217,6 @@ namespace Umbraco.Tests.Services
}
[Test]
public void Cannot_Save_Macro_With_Empty_Name()
{
// Arrange
var macroService = ServiceContext.MacroService;
var macro = new Macro("test", string.Empty, scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234);
// Act & Assert
Assert.Throws<ArgumentException>(() => macroService.Save(macro));
}
//[Test]
//public void Can_Get_Many_By_Alias()
//{
@@ -7,7 +7,6 @@ using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Tests.TestHelpers;
@@ -31,33 +30,6 @@ namespace Umbraco.Tests.Services
base.TearDown();
}
[Test]
public void Get_Paged_Children_With_Media_Type_Filter()
{
var mediaService = ServiceContext.MediaService;
var mediaType1 = MockedContentTypes.CreateImageMediaType("Image2");
ServiceContext.ContentTypeService.Save(mediaType1);
var mediaType2 = MockedContentTypes.CreateImageMediaType("Image3");
ServiceContext.ContentTypeService.Save(mediaType2);
for (int i = 0; i < 10; i++)
{
var m1 = MockedMedia.CreateMediaImage(mediaType1, -1);
mediaService.Save(m1);
var m2 = MockedMedia.CreateMediaImage(mediaType2, -1);
mediaService.Save(m2);
}
long total;
var result = ServiceContext.MediaService.GetPagedChildren(-1, 0, 11, out total, "SortOrder", Direction.Ascending, true, null, new[] {mediaType1.Id, mediaType2.Id});
Assert.AreEqual(11, result.Count());
Assert.AreEqual(20, total);
result = ServiceContext.MediaService.GetPagedChildren(-1, 1, 11, out total, "SortOrder", Direction.Ascending, true, null, new[] { mediaType1.Id, mediaType2.Id });
Assert.AreEqual(9, result.Count());
Assert.AreEqual(20, total);
}
[Test]
public void Can_Move_Media()
{
@@ -109,19 +81,6 @@ namespace Umbraco.Tests.Services
Assert.That(mediaChild.Trashed, Is.False);
}
[Test]
public void Cannot_Save_Media_With_Empty_Name()
{
// Arrange
var mediaService = ServiceContext.MediaService;
var mediaType = MockedContentTypes.CreateVideoMediaType();
ServiceContext.ContentTypeService.Save(mediaType);
var media = mediaService.CreateMedia(string.Empty, -1, "video");
// Act & Assert
Assert.Throws<ArgumentException>(() => mediaService.Save(media));
}
[Test]
public void Ensure_Content_Xml_Created()
{
@@ -185,18 +185,6 @@ namespace Umbraco.Tests.Services
Assert.AreEqual(2, membersInRole.Count());
}
[Test]
public void Cannot_Save_Member_With_Empty_Name()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
ServiceContext.MemberTypeService.Save(memberType);
IMember member = MockedMember.CreateSimpleMember(memberType, string.Empty, "test@test.com", "pass", "test");
// Act & Assert
Assert.Throws<ArgumentException>(() => ServiceContext.MemberService.Save(member));
}
[TestCase("MyTestRole1", "test1", StringPropertyMatchType.StartsWith, 1)]
[TestCase("MyTestRole1", "test", StringPropertyMatchType.StartsWith, 3)]
[TestCase("MyTestRole1", "test1", StringPropertyMatchType.Exact, 1)]
@@ -173,16 +173,6 @@ namespace Umbraco.Tests.Services
}
}
[Test]
public void Cannot_Save_MemberType_With_Empty_Name()
{
// Arrange
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType("memberTypeAlias", string.Empty);
// Act & Assert
Assert.Throws<ArgumentException>(() => ServiceContext.MemberTypeService.Save(memberType));
}
//[Test]
//public void Can_Save_MemberType_Structure_And_Create_A_Member_Based_On_It()
//{
@@ -490,43 +490,6 @@ namespace Umbraco.Tests.Services
Assert.IsTrue(result4.AllowedSections.Contains("test"));
}
[Test]
public void Cannot_Create_User_With_Empty_Username()
{
// Arrange
var userService = ServiceContext.UserService;
var userType = userService.GetUserTypeByAlias("admin");
// Act & Assert
Assert.Throws<ArgumentException>(() => userService.CreateUserWithIdentity(string.Empty, "john@umbraco.io", userType));
}
[Test]
public void Cannot_Save_User_With_Empty_Username()
{
// Arrange
var userService = ServiceContext.UserService;
var userType = userService.GetUserTypeByAlias("admin");
var user = userService.CreateUserWithIdentity("John Doe", "john@umbraco.io", userType);
user.Username = string.Empty;
// Act & Assert
Assert.Throws<ArgumentException>(() => userService.Save(user));
}
[Test]
public void Cannot_Save_User_With_Empty_Name()
{
// Arrange
var userService = ServiceContext.UserService;
var userType = userService.GetUserTypeByAlias("admin");
var user = userService.CreateUserWithIdentity("John Doe", "john@umbraco.io", userType);
user.Name = string.Empty;
// Act & Assert
Assert.Throws<ArgumentException>(() => userService.Save(user));
}
[Test]
public void Get_By_Profile_Username()
{
@@ -6,18 +6,9 @@ namespace Umbraco.Tests.TestHelpers.Entities
{
public class MockedContent
{
public static Content CreateBasicContent(IContentType contentType)
{
var content = new Content("Home", -1, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
content.ResetDirtyProperties(false);
return content;
}
public static Content CreateSimpleContent(IContentType contentType)
{
var content = new Content("Home", -1, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
var content = new Content("Home", -1, contentType) { Language = "en-US", Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
object obj =
new
{
@@ -35,7 +26,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateSimpleContent(IContentType contentType, string name, int parentId)
{
var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 };
var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 };
object obj =
new
{
@@ -53,7 +44,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateSimpleContent(IContentType contentType, string name, IContent parent)
{
var content = new Content(name, parent, contentType) { CreatorId = 0, WriterId = 0 };
var content = new Content(name, parent, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 };
object obj =
new
{
@@ -71,7 +62,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateTextpageContent(IContentType contentType, string name, int parentId)
{
var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0};
var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0};
object obj =
new
{
@@ -90,7 +81,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateSimpleContentWithSpecialDatabaseTypes(IContentType contentType, string name, int parentId, string decimalValue, string intValue, DateTime datetimeValue)
{
var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 };
var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 };
object obj = new
{
decimalProperty = decimalValue,
@@ -105,7 +96,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateAllTypesContent(IContentType contentType, string name, int parentId)
{
var content = new Content("Random Content Name", parentId, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
var content = new Content("Random Content Name", parentId, contentType) { Language = "en-US", Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
content.SetValue("isTrue", true);
content.SetValue("number", 42);
@@ -139,7 +130,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
for (int i = 0; i < amount; i++)
{
var name = "Textpage No-" + i;
var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 };
var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 };
object obj =
new
{
@@ -7,13 +7,6 @@ namespace Umbraco.Tests.TestHelpers.Entities
{
public class MockedContentTypes
{
/// <summary>
/// Creates a content type without any properties
/// </summary>
/// <param name="alias"></param>
/// <param name="name"></param>
/// <param name="parent"></param>
/// <returns></returns>
public static ContentType CreateBasicContentType(string alias = "basePage", string name = "Base Page",
ContentType parent = null)
{
+2 -7
View File
@@ -57,8 +57,8 @@
<Reference Include="AutoMapper.Net4, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.81.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.81\lib\net45\Examine.dll</HintPath>
<Reference Include="Examine, Version=0.1.70.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.70.0\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
@@ -154,20 +154,15 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Dependencies\NuGet.cs" />
<Compile Include="Issues\U9560.cs" />
<Compile Include="Migrations\MigrationIssuesTests.cs" />
<Compile Include="Persistence\BulkDataReaderTests.cs" />
<Compile Include="Persistence\Migrations\MigrationStartupHandlerTests.cs" />
<Compile Include="Persistence\PetaPocoCachesTest.cs" />
<Compile Include="Persistence\PetaPocoExpressionsTests.cs" />
<Compile Include="Persistence\Repositories\EntityRepositoryTest.cs" />
<Compile Include="Persistence\Repositories\RedirectUrlRepositoryTests.cs" />
<Compile Include="Routing\NiceUrlRoutesTests.cs" />
<Compile Include="TestHelpers\Entities\MockedPropertyTypes.cs" />
<Compile Include="TryConvertToTests.cs" />
<Compile Include="UmbracoExamine\RandomIdRAMDirectory.cs" />
<Compile Include="UmbracoExamine\UmbracoContentIndexerTests.cs" />
<Compile Include="Web\AngularIntegration\AngularAntiForgeryTests.cs" />
<Compile Include="Web\AngularIntegration\ContentModelSerializationTests.cs" />
<Compile Include="Web\AngularIntegration\JsInitializationTests.cs" />
+37 -25
View File
@@ -1,8 +1,6 @@
using System;
using System.Linq;
using Examine;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Lucene.Net.Store;
using NUnit.Framework;
using Umbraco.Tests.TestHelpers;
@@ -17,39 +15,53 @@ namespace Umbraco.Tests.UmbracoExamine
[Test]
public void Events_Ignoring_Node()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
//change the parent id so that they are all ignored
var existingCriteria = indexer.IndexerData;
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
999); //change to 999
//change the parent id so that they are all ignored
var existingCriteria = _indexer.IndexerData;
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
999); //change to 999
var isIgnored = false;
var isIgnored = false;
EventHandler<IndexingNodeDataEventArgs> ignoringNode = (s, e) =>
{
isIgnored = true;
};
EventHandler<IndexingNodeDataEventArgs> ignoringNode = (s, e) =>
{
isIgnored = true;
};
indexer.IgnoringNode += ignoringNode;
_indexer.IgnoringNode += ignoringNode;
//get a node from the data repo
var node = _contentService.GetPublishedContentByXPath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.First();
//get a node from the data repo
var node = _contentService.GetPublishedContentByXPath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.First();
indexer.ReIndexNode(node, IndexTypes.Content);
_indexer.ReIndexNode(node, IndexTypes.Content);
Assert.IsTrue(isIgnored);
}
Assert.IsTrue(isIgnored);
}
private readonly TestContentService _contentService = new TestContentService();
private static UmbracoExamineSearcher _searcher;
private static UmbracoContentIndexer _indexer;
private Lucene.Net.Store.Directory _luceneDir;
public override void Initialize()
{
base.Initialize();
_luceneDir = new RAMDirectory();
_indexer = IndexInitializer.GetUmbracoIndexer(_luceneDir);
_indexer.RebuildIndex();
_searcher = IndexInitializer.GetUmbracoSearcher(_luceneDir);
}
public override void TearDown()
{
base.TearDown();
_luceneDir.Dispose();
}
}
}
@@ -7,7 +7,6 @@ using Examine.LuceneEngine.Config;
using Examine.LuceneEngine.Providers;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Lucene.Net.Store;
using Moq;
using Umbraco.Core;
@@ -34,7 +33,7 @@ namespace Umbraco.Tests.UmbracoExamine
internal static class IndexInitializer
{
public static UmbracoContentIndexer GetUmbracoIndexer(
IndexWriter writer,
Directory luceneDir,
Analyzer analyzer = null,
IDataService dataService = null,
IContentService contentService = null,
@@ -42,8 +41,7 @@ namespace Umbraco.Tests.UmbracoExamine
IDataTypeService dataTypeService = null,
IMemberService memberService = null,
IUserService userService = null,
IContentTypeService contentTypeService = null,
bool supportUnpublishedContent = false)
IContentTypeService contentTypeService = null)
{
if (dataService == null)
{
@@ -179,17 +177,15 @@ namespace Umbraco.Tests.UmbracoExamine
var indexCriteria = indexSet.ToIndexCriteria(dataService, UmbracoContentIndexer.IndexFieldPolicies);
var i = new UmbracoContentIndexer(indexCriteria,
writer,
dataService,
contentService,
mediaService,
dataTypeService,
userService,
contentTypeService,
false)
{
SupportUnpublishedContent = supportUnpublishedContent
};
luceneDir, //custom lucene directory
dataService,
contentService,
mediaService,
dataTypeService,
userService,
contentTypeService,
analyzer,
false);
//i.IndexSecondsInterval = 1;
@@ -197,14 +193,13 @@ namespace Umbraco.Tests.UmbracoExamine
return i;
}
public static UmbracoExamineSearcher GetUmbracoSearcher(IndexWriter writer, Analyzer analyzer = null)
public static UmbracoExamineSearcher GetUmbracoSearcher(Directory luceneDir, Analyzer analyzer = null)
{
if (analyzer == null)
{
analyzer = new StandardAnalyzer(Version.LUCENE_29);
}
return new UmbracoExamineSearcher(writer, analyzer);
return new UmbracoExamineSearcher(luceneDir, analyzer);
}
public static LuceneSearcher GetLuceneSearcher(Directory luceneDir)
+169 -181
View File
@@ -1,11 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Examine;
using Examine.LuceneEngine;
using Examine.LuceneEngine.Providers;
using Examine.LuceneEngine.SearchCriteria;
using Examine.SearchCriteria;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
@@ -15,230 +15,218 @@ using UmbracoExamine;
namespace Umbraco.Tests.UmbracoExamine
{
/// <summary>
/// Tests the standard indexing capabilities
/// </summary>
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
[TestFixture, RequiresSTA]
public class IndexTest : ExamineBaseTest
{
/// <summary>
/// Check that the node signalled as protected in the content service is not present in the index.
/// </summary>
[Test]
public void Index_Protected_Content_Not_Indexed()
{
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
///// <summary>
/// <summary>
/// Check that the node signalled as protected in the content service is not present in the index.
/// </summary>
[Test]
public void Index_Protected_Content_Not_Indexed()
{
var protectedQuery = new BooleanQuery();
protectedQuery.Add(
new BooleanClause(
new TermQuery(new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content)),
BooleanClause.Occur.MUST));
var protectedQuery = new BooleanQuery();
protectedQuery.Add(
new BooleanClause(
new TermQuery(new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content)),
BooleanClause.Occur.MUST));
protectedQuery.Add(
new BooleanClause(
new TermQuery(new Term(LuceneIndexer.IndexNodeIdFieldName, TestContentService.ProtectedNode.ToString())),
BooleanClause.Occur.MUST));
protectedQuery.Add(
new BooleanClause(
new TermQuery(new Term(LuceneIndexer.IndexNodeIdFieldName, TestContentService.ProtectedNode.ToString())),
BooleanClause.Occur.MUST));
var collector = new AllHitsCollector(false, true);
var s = searcher.GetSearcher();
s.Search(protectedQuery, collector);
var collector = new AllHitsCollector(false, true);
var s = _searcher.GetSearcher();
s.Search(protectedQuery, collector);
Assert.AreEqual(0, collector.Count, "Protected node should not be indexed");
}
Assert.AreEqual(0, collector.Count, "Protected node should not be indexed");
}
}
[Test]
public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
[Test]
public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID()
{
//change parent id to 1116
var existingCriteria = _indexer.IndexerData;
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
1116);
//rebuild so it excludes children unless they are under 1116
_indexer.RebuildIndex();
var mediaService = new TestMediaService();
//ensure that node 2112 doesn't exist
var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(0, results.Count());
//change parent id to 1116
var existingCriteria = indexer.IndexerData;
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
1116);
//get a node from the data repo (this one exists underneath 2222)
var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.Where(x => (int)x.Attribute("id") == 2112)
.First();
//rebuild so it excludes children unless they are under 1116
indexer.RebuildIndex();
var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112
Assert.AreEqual("-1,1111,2222,2112", currPath);
//ensure that node 2112 doesn't exist
var results = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(0, results.TotalItemCount);
//now mimic moving 2112 to 1116
//node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
node.SetAttributeValue("path", "-1,1116,2112");
node.SetAttributeValue("parentID", "1116");
//get a node from the data repo (this one exists underneath 2222)
var node = mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.First(x => (int)x.Attribute("id") == 2112);
//now reindex the node, this should first delete it and then WILL add it because of the parent id constraint
_indexer.ReIndexNode(node, IndexTypes.Media);
var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112
Assert.AreEqual("-1,1111,2222,2112", currPath);
//RESET the parent id
existingCriteria = ((IndexCriteria)_indexer.IndexerData);
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
null);
//now mimic moving 2112 to 1116
//node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
node.SetAttributeValue("path", "-1,1116,2112");
node.SetAttributeValue("parentID", "1116");
//now ensure it's deleted
var newResults = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(1, newResults.Count());
}
//now reindex the node, this should first delete it and then WILL add it because of the parent id constraint
indexer.ReIndexNode(node, IndexTypes.Media);
[Test]
[Ignore]
public void Index_Move_Media_To_Non_Indexable_ParentID()
{
//get a node from the data repo (this one exists underneath 2222)
var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.Where(x => (int)x.Attribute("id") == 2112)
.First();
//RESET the parent id
existingCriteria = ((IndexCriteria)indexer.IndexerData);
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
null);
var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112
Assert.AreEqual("-1,1111,2222,2112", currPath);
//now ensure it's deleted
var newResults = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(1, newResults.TotalItemCount);
}
//ensure it's indexed
_indexer.ReIndexNode(node, IndexTypes.Media);
}
//change the parent node id to be the one it used to exist under
var existingCriteria = _indexer.IndexerData;
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
2222);
[Test]
public void Index_Move_Media_To_Non_Indexable_ParentID()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
//now mimic moving the node underneath 1116 instead of 2222
node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
node.SetAttributeValue("parentID", "1116");
var mediaService = new TestMediaService();
//now reindex the node, this should first delete it and then NOT add it because of the parent id constraint
_indexer.ReIndexNode(node, IndexTypes.Media);
//get a node from the data repo (this one exists underneath 2222)
var node = mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.First(x => (int)x.Attribute("id") == 2112);
//RESET the parent id
existingCriteria = ((IndexCriteria)_indexer.IndexerData);
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
null);
var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112
Assert.AreEqual("-1,1111,2222,2112", currPath);
//now ensure it's deleted
var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(0, results.Count());
//ensure it's indexed
indexer.ReIndexNode(node, IndexTypes.Media);
//change the parent node id to be the one it used to exist under
var existingCriteria = indexer.IndexerData;
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
2222);
//now mimic moving the node underneath 1116 instead of 2222
node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
node.SetAttributeValue("parentID", "1116");
//now reindex the node, this should first delete it and then NOT add it because of the parent id constraint
indexer.ReIndexNode(node, IndexTypes.Media);
//RESET the parent id
existingCriteria = ((IndexCriteria)indexer.IndexerData);
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
null);
//now ensure it's deleted
var results = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(0, results.TotalItemCount);
}
}
}
/// <summary>
/// This will ensure that all 'Content' (not media) is cleared from the index using the Lucene API directly.
/// We then call the Examine method to re-index Content and do some comparisons to ensure that it worked correctly.
/// </summary>
[Test]
public void Index_Reindex_Content()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer, supportUnpublishedContent: true))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
/// <summary>
/// This will ensure that all 'Content' (not media) is cleared from the index using the Lucene API directly.
/// We then call the Examine method to re-index Content and do some comparisons to ensure that it worked correctly.
/// </summary>
[Test]
public void Index_Reindex_Content()
{
var s = (IndexSearcher)_searcher.GetSearcher();
var s = (IndexSearcher)searcher.GetSearcher();
//first delete all 'Content' (not media). This is done by directly manipulating the index with the Lucene API, not examine!
var contentTerm = new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content);
var writer = _indexer.GetIndexWriter();
writer.DeleteDocuments(contentTerm);
writer.Commit();
//first delete all 'Content' (not media). This is done by directly manipulating the index with the Lucene API, not examine!
//make sure the content is gone. This is done with lucene APIs, not examine!
var collector = new AllHitsCollector(false, true);
var query = new TermQuery(contentTerm);
s = (IndexSearcher)_searcher.GetSearcher(); //make sure the searcher is up do date.
s.Search(query, collector);
Assert.AreEqual(0, collector.Count);
var contentTerm = new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content);
writer.DeleteDocuments(contentTerm);
writer.Commit();
//call our indexing methods
_indexer.IndexAll(IndexTypes.Content);
//make sure the content is gone. This is done with lucene APIs, not examine!
var collector = new AllHitsCollector(false, true);
var query = new TermQuery(contentTerm);
s = (IndexSearcher)searcher.GetSearcher(); //make sure the searcher is up do date.
s.Search(query, collector);
Assert.AreEqual(0, collector.Count);
collector = new AllHitsCollector(false, true);
s = (IndexSearcher)_searcher.GetSearcher(); //make sure the searcher is up do date.
s.Search(query, collector);
//var ids = new List<string>();
//for (var i = 0; i < collector.Count;i++)
//{
// ids.Add(s.Doc(collector.GetDocId(i)).GetValues("__NodeId")[0]);
//}
Assert.AreEqual(20, collector.Count);
}
//call our indexing methods
indexer.IndexAll(IndexTypes.Content);
/// <summary>
/// This will delete an item from the index and ensure that all children of the node are deleted too!
/// </summary>
[Test]
[Ignore]
public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed()
{
collector = new AllHitsCollector(false, true);
s = (IndexSearcher)searcher.GetSearcher(); //make sure the searcher is up do date.
s.Search(query, collector);
//var ids = new List<string>();
//for (var i = 0; i < collector.Count;i++)
//{
// ids.Add(s.Doc(collector.GetDocId(i)).GetValues("__NodeId")[0]);
//}
Assert.AreEqual(21, collector.Count);
}
}
//now delete a node that has children
/// <summary>
/// This will delete an item from the index and ensure that all children of the node are deleted too!
/// </summary>
[Test]
public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed()
{
_indexer.DeleteFromIndex(1140.ToString());
//this node had children: 1141 & 1142, let's ensure they are also removed
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(1141).Compile());
Assert.AreEqual(0, results.Count());
//now delete a node that has children
results = _searcher.Search(_searcher.CreateSearchCriteria().Id(1142).Compile());
Assert.AreEqual(0, results.Count());
indexer.DeleteFromIndex(1140.ToString());
//this node had children: 1141 & 1142, let's ensure they are also removed
}
var results = searcher.Search(searcher.CreateSearchCriteria().Id(1141).Compile());
Assert.AreEqual(0, results.TotalItemCount);
#region Private methods and properties
results = searcher.Search(searcher.CreateSearchCriteria().Id(1142).Compile());
Assert.AreEqual(0, results.TotalItemCount);
}
}
#region Initialize and Cleanup
public override void TearDown()
{
base.TearDown();
private readonly TestContentService _contentService = new TestContentService();
private readonly TestMediaService _mediaService = new TestMediaService();
private static UmbracoExamineSearcher _searcher;
private static UmbracoContentIndexer _indexer;
#endregion
#region Initialize and Cleanup
private Lucene.Net.Store.Directory _luceneDir;
public override void TearDown()
{
base.TearDown();
_luceneDir.Dispose();
UmbracoExamineSearcher.DisableInitializationCheck = null;
BaseUmbracoIndexer.DisableInitializationCheck = null;
}
#endregion
}
public override void Initialize()
{
base.Initialize();
_luceneDir = new RAMDirectory();
_indexer = IndexInitializer.GetUmbracoIndexer(_luceneDir);
_indexer.RebuildIndex();
_searcher = IndexInitializer.GetUmbracoSearcher(_luceneDir);
}
#endregion
}
}
@@ -1,14 +0,0 @@
using System;
using Lucene.Net.Store;
namespace Umbraco.Tests.UmbracoExamine
{
public class RandomIdRAMDirectory : RAMDirectory
{
private readonly string _lockId = Guid.NewGuid().ToString();
public override string GetLockID()
{
return _lockId;
}
}
}
+31 -13
View File
@@ -8,8 +8,6 @@ using Examine.LuceneEngine.Providers;
using Lucene.Net.Store;
using NUnit.Framework;
using Examine.LuceneEngine.SearchCriteria;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.UmbracoExamine
@@ -22,17 +20,19 @@ namespace Umbraco.Tests.UmbracoExamine
[Test]
public void Test_Sort_Order_Sorting()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer, null,
//var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\SearchTests", Guid.NewGuid().ToString()));
//System.IO.Directory.CreateDirectory(newIndexFolder.FullName);
using (var luceneDir = new RAMDirectory())
//using (var luceneDir = new SimpleFSDirectory(newIndexFolder))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir, null,
new TestDataService()
{
ContentService = new TestContentService(TestFiles.umbraco_sort)
},
supportUnpublishedContent: true))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
{
ContentService = new TestContentService(TestFiles.umbraco_sort)
});
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var s = (LuceneSearcher)searcher;
var luceneSearcher = s.GetSearcher();
@@ -69,7 +69,25 @@ namespace Umbraco.Tests.UmbracoExamine
currentSort = sort;
}
return true;
}
}
//[Test]
//public void Test_Index_Type_With_German_Analyzer()
//{
// using (var luceneDir = new RAMDirectory())
// {
// var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir,
// new GermanAnalyzer());
// indexer.RebuildIndex();
// var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
// }
//}
//private readonly TestContentService _contentService = new TestContentService();
//private readonly TestMediaService _mediaService = new TestMediaService();
//private static UmbracoExamineSearcher _searcher;
//private static UmbracoContentIndexer _indexer;
//private Lucene.Net.Store.Directory _luceneDir;
}
}
@@ -1,107 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using UmbracoExamine;
namespace Umbraco.Tests.UmbracoExamine
{
[TestFixture]
public class UmbracoContentIndexerTests : ExamineBaseTest
{
[Test]
public void Get_Serialized_Content_No_Published_Content()
{
var contentSet = new List<IContent>
{
Mock.Of<IContent>(c => c.Id == 1 && c.Path == "-1,1" && c.Published && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 2 && c.Path == "-1,2" && c.Published && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 3 && c.Path == "-1,3" && c.Published == false && c.Level == 1), // no
Mock.Of<IContent>(c => c.Id == 4 && c.Path == "-1,4" && c.Published == false && c.Level == 1), // no
Mock.Of<IContent>(c => c.Id == 5 && c.Path == "-1,1,5" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 6 && c.Path == "-1,2,6" && c.Published == false && c.Level == 2), // no
Mock.Of<IContent>(c => c.Id == 7 && c.Path == "-1,3,7" && c.Published && c.Level == 2), // no
Mock.Of<IContent>(c => c.Id == 8 && c.Path == "-1,4,8" && c.Published && c.Level == 2), // no
Mock.Of<IContent>(c => c.Id == 9 && c.Path == "-1,4,9" && c.Published && c.Level == 2), // no
Mock.Of<IContent>(c => c.Id == 10 && c.Path == "-1,1,5,10" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 15 && c.Path == "-1,1,5,15" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 11 && c.Path == "-1,2,6,11" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 16 && c.Path == "-1,2,6,16" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 12 && c.Path == "-1,3,7,12" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 17 && c.Path == "-1,3,7,17" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 13 && c.Path == "-1,4,8,13" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 18 && c.Path == "-1,4,8,18" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 14 && c.Path == "-1,4,9,14" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 19 && c.Path == "-1,4,9,19" && c.Published && c.Level == 3), // no
};
//ensure the rest of the required values are populted
foreach (var content in contentSet)
{
var mock = Mock.Get(content);
mock.Setup(x => x.ContentType).Returns(Mock.Of<IContentType>(type => type.Icon == "hello"));
}
contentSet.Sort((a, b) => Comparer<int>.Default.Compare(a.Level, b.Level));
var published = new HashSet<string>();
var result = UmbracoContentIndexer.GetSerializedContent(false, content => new XElement("test"), contentSet, published)
.WhereNotNull()
.ToArray();
Assert.AreEqual(5, result.Length);
}
[Test]
public void Get_Serialized_Content_With_Published_Content()
{
var contentSet = new List<IContent>
{
Mock.Of<IContent>(c => c.Id == 1 && c.Path == "-1,1" && c.Published && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 2 && c.Path == "-1,2" && c.Published && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 3 && c.Path == "-1,3" && c.Published == false && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 4 && c.Path == "-1,4" && c.Published == false && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 5 && c.Path == "-1,1,5" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 6 && c.Path == "-1,2,6" && c.Published == false && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 7 && c.Path == "-1,3,7" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 8 && c.Path == "-1,4,8" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 9 && c.Path == "-1,4,9" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 10 && c.Path == "-1,1,5,10" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 15 && c.Path == "-1,1,5,15" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 11 && c.Path == "-1,2,6,11" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 16 && c.Path == "-1,2,6,16" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 12 && c.Path == "-1,3,7,12" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 17 && c.Path == "-1,3,7,17" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 13 && c.Path == "-1,4,8,13" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 18 && c.Path == "-1,4,8,18" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 14 && c.Path == "-1,4,9,14" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 19 && c.Path == "-1,4,9,19" && c.Published && c.Level == 3),
};
//ensure the rest of the required values are populted
foreach (var content in contentSet)
{
var mock = Mock.Get(content);
mock.Setup(x => x.ContentType).Returns(Mock.Of<IContentType>(type => type.Icon == "hello"));
}
contentSet.Sort((a, b) => Comparer<int>.Default.Compare(a.Level, b.Level));
var published = new HashSet<string>();
var result = UmbracoContentIndexer.GetSerializedContent(true, content => new XElement("test"), contentSet, published)
.WhereNotNull()
.ToArray();
Assert.AreEqual(19, result.Length);
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
<packages>
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="Examine" version="0.1.81" targetFramework="net45" />
<package id="Examine" version="0.1.70.0" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
@@ -427,9 +427,7 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
* Retrieves all media children with types used as folders.
* Uses the convention of looking for media items with mediaTypes ending in
* *Folder so will match "Folder", "bannerFolder", "secureFolder" etc,
*
* NOTE: This will return a max of 500 folders, if more is required it needs to be paged
*
*
* ##usage
* <pre>
* mediaResource.getChildFolders(1234)
@@ -447,15 +445,14 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
parentId = -1;
}
//NOTE: This will return a max of 500 folders, if more is required it needs to be paged
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetChildFolders",
{
id: parentId
})),
[
{ id: parentId }
])),
'Failed to retrieve child folders for media item ' + parentId);
},
@@ -19,20 +19,15 @@ angular.module('umbraco.security.interceptor')
return promise;
}, function(originalResponse) {
// Intercept failed requests
// Make sure we have the configuration of the request (don't we always?)
var config = originalResponse.config ? originalResponse.config : {};
// Make sure we have an object for the headers of the request
var headers = config.headers ? config.headers : {};
//Here we'll check if we should ignore the error (either based on the original header set or the request configuration)
if (headers["x-umb-ignore-error"] === "ignore" || config.umbIgnoreErrors === true) {
//Here we'll check if we should ignore the error, this will be based on an original header set
var headers = originalResponse.config ? originalResponse.config.headers : {};
if (headers["x-umb-ignore-error"] === "ignore") {
//exit/ignore
return promise;
}
var filtered = _.find(requestInterceptorFilter(), function(val) {
return config.url.indexOf(val) > 0;
return originalResponse.config.url.indexOf(val) > 0;
});
if (filtered) {
return promise;
@@ -104,4 +99,4 @@ angular.module('umbraco.security.interceptor')
// We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block.
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('securityInterceptor');
}]);
}]);
@@ -7,19 +7,6 @@ function mediaTypeHelper(mediaTypeResource, $q) {
var mediaTypeHelperService = {
isFolderType: function(mediaEntity) {
if (!mediaEntity) {
throw "mediaEntity is null";
}
if (!mediaEntity.contentTypeAlias) {
throw "mediaEntity.contentTypeAlias is null";
}
//if you create a media type, which has an alias that ends with ...Folder then its a folder: ex: "secureFolder", "bannerFolder", "Folder"
//this is the exact same logic that is performed in MediaController.GetChildFolders
return mediaEntity.contentTypeAlias.endsWith("Folder");
},
getAllowedImagetypes: function (mediaId){
// Get All allowedTypes
@@ -204,7 +204,7 @@ angular.module('umbraco.services')
//when it's successful, return the user data
setCurrentUser(data);
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
var result = { user: data, authenticated: true, lastUserId: lastUserId };
//broadcast a global event
eventsService.emit("app.authenticated", result);
@@ -232,7 +232,7 @@ angular.module('umbraco.services')
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
var result = { user: data, authenticated: true, lastUserId: lastUserId };
//TODO: This is a mega backwards compatibility hack... These variables SHOULD NOT exist in the server variables
// since they are not supposed to be dynamic but I accidentally added them there in 7.1.5 IIRC so some people might
@@ -8,7 +8,7 @@
* The main application controller
*
*/
function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale, localStorageService) {
function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale) {
//the null is important because we do an explicit bool check on this in the view
//the avatar is by default the umbraco logo
@@ -81,14 +81,6 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
$location.path("/").search("");
historyService.removeAll();
treeService.clearCache();
//if the user changed, clearout local storage too - could contain sensitive data
localStorageService.clearAll();
}
//if this is a new login (i.e. the user entered credentials), then clear out local storage - could contain sensitive data
if (data.loginType === "credentials") {
localStorageService.clearAll();
}
//Load locale file
+2 -3
View File
@@ -1,6 +1,6 @@
/** Executed when the application starts, binds to events and set global state */
app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', 'appState', 'editorState', 'fileManager', 'assetsService', 'eventsService', '$cookies', '$templateCache', 'localStorageService',
function (userService, $log, $rootScope, $location, navigationService, appState, editorState, fileManager, assetsService, eventsService, $cookies, $templateCache, localStorageService) {
app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', 'appState', 'editorState', 'fileManager', 'assetsService', 'eventsService', '$cookies', '$templateCache',
function (userService, $log, $rootScope, $location, navigationService, appState, editorState, fileManager, assetsService, eventsService, $cookies, $templateCache) {
//This sets the default jquery ajax headers to include our csrf token, we
// need to user the beforeSend method because our token changes per user/login so
@@ -13,7 +13,6 @@ app.run(['userService', '$log', '$rootScope', '$location', 'navigationService',
/** Listens for authentication and checks if our required assets are loaded, if/once they are we'll broadcast a ready event */
eventsService.on("app.authenticated", function(evt, data) {
assetsService._loadInitAssets().then(function() {
appState.setGlobalState("isReady", true);

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