2016-05-27 14:26:28 +02:00
using System ;
using System.Collections.Generic ;
2017-11-21 14:19:16 +01:00
using System.Diagnostics ;
2016-05-27 14:26:28 +02:00
using System.Linq ;
using Newtonsoft.Json ;
using NPoco ;
using Umbraco.Core ;
using Umbraco.Core.Logging ;
2017-11-21 14:19:16 +01:00
using Umbraco.Core.Persistence ;
2017-12-28 09:06:33 +01:00
using Umbraco.Core.Persistence.Dtos ;
2017-12-12 15:04:13 +01:00
using Umbraco.Core.Scoping ;
2016-05-27 14:26:28 +02:00
using Umbraco.Core.Serialization ;
2017-05-30 18:13:11 +02:00
using Umbraco.Web.Composing ;
2017-11-21 14:19:16 +01:00
using static Umbraco . Core . Persistence . NPocoSqlExtensions . Statics ;
2016-05-27 14:26:28 +02:00
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
{
2017-11-21 14:19:16 +01:00
// fixme - use SqlTemplate for these queries else it's going to be horribly slow!
2016-05-27 14:26:28 +02:00
// provides efficient database access for NuCache
2018-04-18 19:46:47 +02:00
internal class DatabaseDataSource : IDataSource
2016-05-27 14:26:28 +02:00
{
2017-12-07 13:22:32 +01:00
// we want arrays, we want them all loaded, not an enumerable
2017-12-12 15:04:13 +01:00
private Sql < ISqlContext > ContentSourcesSelect ( IScope scope , Func < Sql < ISqlContext > , Sql < ISqlContext > > joins = null )
2017-11-21 14:19:16 +01:00
{
2017-12-12 15:04:13 +01:00
var sql = scope . SqlContext . Sql ( )
2017-11-21 14:19:16 +01:00
. Select < NodeDto > ( x = > Alias ( x . NodeId , "Id" ) , x = > Alias ( x . UniqueId , "Uid" ) ,
x = > Alias ( x . Level , "Level" ) , x = > Alias ( x . Path , "Path" ) , x = > Alias ( x . SortOrder , "SortOrder" ) , x = > Alias ( x . ParentId , "ParentId" ) ,
x = > Alias ( x . CreateDate , "CreateDate" ) , x = > Alias ( x . UserId , "CreatorId" ) )
. AndSelect < ContentDto > ( x = > Alias ( x . ContentTypeId , "ContentTypeId" ) )
. AndSelect < DocumentDto > ( x = > Alias ( x . Published , "Published" ) , x = > Alias ( x . Edited , "Edited" ) )
2017-12-07 13:22:32 +01:00
. AndSelect < ContentVersionDto > ( x = > Alias ( x . Id , "VersionId" ) , x = > Alias ( x . Text , "EditName" ) , x = > Alias ( x . VersionDate , "EditVersionDate" ) , x = > Alias ( x . UserId , "EditWriterId" ) )
. AndSelect < DocumentVersionDto > ( x = > Alias ( x . TemplateId , "EditTemplateId" ) )
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. AndSelect < ContentVersionDto > ( "pcver" , x = > Alias ( x . Id , "PublishedVersionId" ) , x = > Alias ( x . Text , "PubName" ) , x = > Alias ( x . VersionDate , "PubVersionDate" ) , x = > Alias ( x . UserId , "PubWriterId" ) )
2017-11-21 14:19:16 +01:00
. AndSelect < DocumentVersionDto > ( "pdver" , x = > Alias ( x . TemplateId , "PubTemplateId" ) )
2017-12-07 13:22:32 +01:00
. AndSelect < ContentNuDto > ( "nuEdit" , x = > Alias ( x . Data , "EditData" ) )
. AndSelect < ContentNuDto > ( "nuPub" , x = > Alias ( x . Data , "PubData" ) )
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. From < NodeDto > ( ) ;
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
if ( joins ! = null )
sql = joins ( sql ) ;
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
sql = sql
. InnerJoin < ContentDto > ( ) . On < NodeDto , ContentDto > ( ( left , right ) = > left . NodeId = = right . NodeId )
. InnerJoin < DocumentDto > ( ) . On < NodeDto , DocumentDto > ( ( left , right ) = > left . NodeId = = right . NodeId )
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. InnerJoin < ContentVersionDto > ( ) . On < NodeDto , ContentVersionDto > ( ( left , right ) = > left . NodeId = = right . NodeId & & right . Current )
. InnerJoin < DocumentVersionDto > ( ) . On < ContentVersionDto , DocumentVersionDto > ( ( left , right ) = > left . Id = = right . Id )
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. LeftJoin < ContentVersionDto > ( j = >
j . InnerJoin < DocumentVersionDto > ( "pdver" ) . On < ContentVersionDto , DocumentVersionDto > ( ( left , right ) = > left . Id = = right . Id & & right . Published , "pcver" , "pdver" ) , "pcver" )
. On < NodeDto , ContentVersionDto > ( ( left , right ) = > left . NodeId = = right . NodeId , aliasRight : "pcver" )
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. LeftJoin < ContentNuDto > ( "nuEdit" ) . On < NodeDto , ContentNuDto > ( ( left , right ) = > left . NodeId = = right . NodeId & & ! right . Published , aliasRight : "nuEdit" )
. LeftJoin < ContentNuDto > ( "nuPub" ) . On < NodeDto , ContentNuDto > ( ( left , right ) = > left . NodeId = = right . NodeId & & right . Published , aliasRight : "nuPub" ) ;
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
return sql ;
2016-05-27 14:26:28 +02:00
}
2017-12-12 15:04:13 +01:00
public ContentNodeKit GetContentSource ( IScope scope , int id )
2016-05-27 14:26:28 +02:00
{
2017-12-12 15:04:13 +01:00
var sql = ContentSourcesSelect ( scope )
2018-05-31 17:14:25 +02:00
. Where < NodeDto > ( x = > x . NodeObjectType = = Constants . ObjectTypes . Document & & x . NodeId = = id & & ! x . Trashed )
2017-12-07 13:22:32 +01:00
. OrderBy < NodeDto > ( x = > x . Level , x = > x . SortOrder ) ;
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
var dto = scope . Database . Fetch < ContentSourceDto > ( sql ) . FirstOrDefault ( ) ;
2017-11-21 14:19:16 +01:00
return dto = = null ? new ContentNodeKit ( ) : CreateContentNodeKit ( dto ) ;
2016-05-27 14:26:28 +02:00
}
2017-12-12 15:04:13 +01:00
public IEnumerable < ContentNodeKit > GetAllContentSources ( IScope scope )
2016-05-27 14:26:28 +02:00
{
2017-12-12 15:04:13 +01:00
var sql = ContentSourcesSelect ( scope )
2018-05-31 17:14:25 +02:00
. Where < NodeDto > ( x = > x . NodeObjectType = = Constants . ObjectTypes . Document & & ! x . Trashed )
2017-12-07 13:22:32 +01:00
. OrderBy < NodeDto > ( x = > x . Level , x = > x . SortOrder ) ;
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
return scope . Database . Query < ContentSourceDto > ( sql ) . Select ( CreateContentNodeKit ) ;
2016-05-27 14:26:28 +02:00
}
2017-12-12 15:04:13 +01:00
public IEnumerable < ContentNodeKit > GetBranchContentSources ( IScope scope , int id )
2016-05-27 14:26:28 +02:00
{
2017-12-12 15:04:13 +01:00
var syntax = scope . SqlContext . SqlSyntax ;
var sql = ContentSourcesSelect ( scope , s = > s
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. InnerJoin < NodeDto > ( "x" ) . On < NodeDto , NodeDto > ( ( left , right ) = > left . NodeId = = right . NodeId | | SqlText < bool > ( left . Path , right . Path , ( lp , rp ) = > $"({lp} LIKE {syntax.GetConcat(rp, " ' , % ' ")})" ) , aliasRight : "x" ) )
2017-11-21 14:19:16 +01:00
2018-05-31 17:14:25 +02:00
. Where < NodeDto > ( x = > x . NodeObjectType = = Constants . ObjectTypes . Document & & ! x . Trashed )
2017-11-21 14:19:16 +01:00
. Where < NodeDto > ( x = > x . NodeId = = id , "x" )
2017-12-07 13:22:32 +01:00
. OrderBy < NodeDto > ( x = > x . Level , x = > x . SortOrder ) ;
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
return scope . Database . Query < ContentSourceDto > ( sql ) . Select ( CreateContentNodeKit ) ;
2016-05-27 14:26:28 +02:00
}
2017-12-12 15:04:13 +01:00
public IEnumerable < ContentNodeKit > GetTypeContentSources ( IScope scope , IEnumerable < int > ids )
2016-05-27 14:26:28 +02:00
{
2017-12-12 15:04:13 +01:00
var sql = ContentSourcesSelect ( scope )
2018-05-31 17:14:25 +02:00
. Where < NodeDto > ( x = > x . NodeObjectType = = Constants . ObjectTypes . Document & & ! x . Trashed )
2017-12-07 13:22:32 +01:00
. WhereIn < ContentDto > ( x = > x . ContentTypeId , ids )
. OrderBy < NodeDto > ( x = > x . Level , x = > x . SortOrder ) ;
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
return scope . Database . Query < ContentSourceDto > ( sql ) . Select ( CreateContentNodeKit ) ;
2017-12-07 13:22:32 +01:00
}
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
private Sql < ISqlContext > MediaSourcesSelect ( IScope scope , Func < Sql < ISqlContext > , Sql < ISqlContext > > joins = null )
2017-12-07 13:22:32 +01:00
{
2017-12-12 15:04:13 +01:00
var sql = scope . SqlContext . Sql ( )
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. Select < NodeDto > ( x = > Alias ( x . NodeId , "Id" ) , x = > Alias ( x . UniqueId , "Uid" ) ,
x = > Alias ( x . Level , "Level" ) , x = > Alias ( x . Path , "Path" ) , x = > Alias ( x . SortOrder , "SortOrder" ) , x = > Alias ( x . ParentId , "ParentId" ) ,
x = > Alias ( x . CreateDate , "CreateDate" ) , x = > Alias ( x . UserId , "CreatorId" ) )
. AndSelect < ContentDto > ( x = > Alias ( x . ContentTypeId , "ContentTypeId" ) )
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. AndSelect < ContentVersionDto > ( x = > Alias ( x . Id , "VersionId" ) , x = > Alias ( x . Text , "EditName" ) , x = > Alias ( x . VersionDate , "EditVersionDate" ) , x = > Alias ( x . UserId , "EditWriterId" ) )
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. AndSelect < ContentNuDto > ( "nuEdit" , x = > Alias ( x . Data , "EditData" ) )
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. From < NodeDto > ( ) ;
2016-05-27 14:26:28 +02:00
2017-12-07 13:22:32 +01:00
if ( joins ! = null )
sql = joins ( sql ) ;
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
sql = sql
2017-11-21 14:19:16 +01:00
. InnerJoin < ContentDto > ( ) . On < NodeDto , ContentDto > ( ( left , right ) = > left . NodeId = = right . NodeId )
. InnerJoin < ContentVersionDto > ( ) . On < NodeDto , ContentVersionDto > ( ( left , right ) = > left . NodeId = = right . NodeId & & right . Current )
2017-12-07 13:22:32 +01:00
. LeftJoin < ContentNuDto > ( "nuEdit" ) . On < NodeDto , ContentNuDto > ( ( left , right ) = > left . NodeId = = right . NodeId & & ! right . Published , aliasRight : "nuEdit" ) ;
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
return sql ;
}
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
public ContentNodeKit GetMediaSource ( IScope scope , int id )
2017-12-07 13:22:32 +01:00
{
2017-12-12 15:04:13 +01:00
var sql = MediaSourcesSelect ( scope )
2018-05-31 17:14:25 +02:00
. Where < NodeDto > ( x = > x . NodeObjectType = = Constants . ObjectTypes . Media & & x . NodeId = = id & & ! x . Trashed )
2017-12-07 13:22:32 +01:00
. OrderBy < NodeDto > ( x = > x . Level , x = > x . SortOrder ) ;
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
var dto = scope . Database . Fetch < ContentSourceDto > ( sql ) . FirstOrDefault ( ) ;
2017-12-07 13:22:32 +01:00
return dto = = null ? new ContentNodeKit ( ) : CreateContentNodeKit ( dto ) ;
}
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
public IEnumerable < ContentNodeKit > GetAllMediaSources ( IScope scope )
2017-12-07 13:22:32 +01:00
{
2017-12-12 15:04:13 +01:00
var sql = MediaSourcesSelect ( scope )
2018-05-31 17:14:25 +02:00
. Where < NodeDto > ( x = > x . NodeObjectType = = Constants . ObjectTypes . Media & & ! x . Trashed )
2017-12-07 13:22:32 +01:00
. OrderBy < NodeDto > ( x = > x . Level , x = > x . SortOrder ) ;
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
return scope . Database . Query < ContentSourceDto > ( sql ) . Select ( CreateMediaNodeKit ) ;
2016-05-27 14:26:28 +02:00
}
2017-12-12 15:04:13 +01:00
public IEnumerable < ContentNodeKit > GetBranchMediaSources ( IScope scope , int id )
2016-05-27 14:26:28 +02:00
{
2017-12-12 15:04:13 +01:00
var syntax = scope . SqlContext . SqlSyntax ;
var sql = MediaSourcesSelect ( scope , s = > s
2017-11-21 14:19:16 +01:00
2017-12-07 13:22:32 +01:00
. InnerJoin < NodeDto > ( "x" ) . On < NodeDto , NodeDto > ( ( left , right ) = > left . NodeId = = right . NodeId | | SqlText < bool > ( left . Path , right . Path , ( lp , rp ) = > $"({lp} LIKE {syntax.GetConcat(rp, " ' , % ' ")})" ) , aliasRight : "x" ) )
2017-11-21 14:19:16 +01:00
2018-05-31 17:14:25 +02:00
. Where < NodeDto > ( x = > x . NodeObjectType = = Constants . ObjectTypes . Media & & ! x . Trashed )
2017-12-07 13:22:32 +01:00
. Where < NodeDto > ( x = > x . NodeId = = id , "x" )
. OrderBy < NodeDto > ( x = > x . Level , x = > x . SortOrder ) ;
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
return scope . Database . Query < ContentSourceDto > ( sql ) . Select ( CreateMediaNodeKit ) ;
2017-12-07 13:22:32 +01:00
}
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
public IEnumerable < ContentNodeKit > GetTypeMediaSources ( IScope scope , IEnumerable < int > ids )
2017-12-07 13:22:32 +01:00
{
2017-12-12 15:04:13 +01:00
var sql = MediaSourcesSelect ( scope )
2018-05-31 17:14:25 +02:00
. Where < NodeDto > ( x = > x . NodeObjectType = = Constants . ObjectTypes . Media & & ! x . Trashed )
2017-11-21 14:19:16 +01:00
. WhereIn < ContentDto > ( x = > x . ContentTypeId , ids )
2017-12-07 13:22:32 +01:00
. OrderBy < NodeDto > ( x = > x . Level , x = > x . SortOrder ) ;
2017-11-21 14:19:16 +01:00
2017-12-12 15:04:13 +01:00
return scope . Database . Query < ContentSourceDto > ( sql ) . Select ( CreateMediaNodeKit ) ;
2016-05-27 14:26:28 +02:00
}
private static ContentNodeKit CreateContentNodeKit ( ContentSourceDto dto )
{
ContentData d = null ;
ContentData p = null ;
2017-12-07 13:22:32 +01:00
if ( dto . EditData = = null )
2016-05-27 14:26:28 +02:00
{
2017-12-07 13:22:32 +01:00
if ( Debugger . IsAttached )
throw new Exception ( "Missing cmsContentNu edited content for node " + dto . Id + ", consider rebuilding." ) ;
2018-04-18 19:46:47 +02:00
Current . Logger . Warn < DatabaseDataSource > ( "Missing cmsContentNu edited content for node " + dto . Id + ", consider rebuilding." ) ;
2017-12-07 13:22:32 +01:00
}
else
{
2018-04-25 15:55:27 +02:00
var nested = DeserializeNestedData ( dto . EditData ) ;
2018-04-24 01:31:01 +10:00
2017-12-07 13:22:32 +01:00
d = new ContentData
2016-05-27 14:26:28 +02:00
{
2017-12-07 13:22:32 +01:00
Name = dto . EditName ,
Published = false ,
TemplateId = dto . EditTemplateId ,
VersionId = dto . VersionId ,
VersionDate = dto . EditVersionDate ,
WriterId = dto . EditWriterId ,
2018-04-25 15:55:27 +02:00
Properties = nested . PropertyData ,
CultureInfos = nested . CultureData
2017-12-07 13:22:32 +01:00
} ;
2016-05-27 14:26:28 +02:00
}
2017-11-15 08:53:20 +01:00
if ( dto . Published )
2016-05-27 14:26:28 +02:00
{
if ( dto . PubData = = null )
{
2017-11-21 14:19:16 +01:00
if ( Debugger . IsAttached )
throw new Exception ( "Missing cmsContentNu published content for node " + dto . Id + ", consider rebuilding." ) ;
2018-04-18 19:46:47 +02:00
Current . Logger . Warn < DatabaseDataSource > ( "Missing cmsContentNu published content for node " + dto . Id + ", consider rebuilding." ) ;
2016-05-27 14:26:28 +02:00
}
else
{
2018-04-25 15:55:27 +02:00
var nested = DeserializeNestedData ( dto . PubData ) ;
2018-04-24 01:31:01 +10:00
2016-05-27 14:26:28 +02:00
p = new ContentData
{
Name = dto . PubName ,
Published = true ,
TemplateId = dto . PubTemplateId ,
2017-12-07 13:22:32 +01:00
VersionId = dto . VersionId ,
2016-05-27 14:26:28 +02:00
VersionDate = dto . PubVersionDate ,
WriterId = dto . PubWriterId ,
2018-04-25 15:55:27 +02:00
Properties = nested . PropertyData ,
CultureInfos = nested . CultureData
2016-05-27 14:26:28 +02:00
} ;
}
}
var n = new ContentNode ( dto . Id , dto . Uid ,
dto . Level , dto . Path , dto . SortOrder , dto . ParentId , dto . CreateDate , dto . CreatorId ) ;
var s = new ContentNodeKit
{
Node = n ,
ContentTypeId = dto . ContentTypeId ,
DraftData = d ,
PublishedData = p
} ;
return s ;
}
private static ContentNodeKit CreateMediaNodeKit ( ContentSourceDto dto )
{
2017-12-07 13:22:32 +01:00
if ( dto . EditData = = null )
2016-05-27 14:26:28 +02:00
throw new Exception ( "No data for media " + dto . Id ) ;
2018-04-25 15:55:27 +02:00
var nested = DeserializeNestedData ( dto . EditData ) ;
2018-04-24 01:31:01 +10:00
2016-05-27 14:26:28 +02:00
var p = new ContentData
{
2017-12-07 13:22:32 +01:00
Name = dto . EditName ,
2016-05-27 14:26:28 +02:00
Published = true ,
TemplateId = - 1 ,
2017-12-07 13:22:32 +01:00
VersionId = dto . VersionId ,
VersionDate = dto . EditVersionDate ,
2016-05-27 14:26:28 +02:00
WriterId = dto . CreatorId , // what-else?
2018-04-25 15:55:27 +02:00
Properties = nested . PropertyData ,
CultureInfos = nested . CultureData
2016-05-27 14:26:28 +02:00
} ;
var n = new ContentNode ( dto . Id , dto . Uid ,
dto . Level , dto . Path , dto . SortOrder , dto . ParentId , dto . CreateDate , dto . CreatorId ) ;
var s = new ContentNodeKit
{
Node = n ,
ContentTypeId = dto . ContentTypeId ,
PublishedData = p
} ;
return s ;
}
2018-04-25 15:55:27 +02:00
private static ContentNestedData DeserializeNestedData ( string data )
2016-05-27 14:26:28 +02:00
{
// by default JsonConvert will deserialize our numeric values as Int64
// which is bad, because they were Int32 in the database - take care
var settings = new JsonSerializerSettings
{
Converters = new List < JsonConverter > { new ForceInt32Converter ( ) }
} ;
2018-04-25 15:55:27 +02:00
return JsonConvert . DeserializeObject < ContentNestedData > ( data , settings ) ;
2016-05-27 14:26:28 +02:00
}
}
}