2020-12-09 22:43:49 +11:00
using System ;
2017-12-07 16:45:25 +01:00
using System.Collections.Generic ;
using System.Linq ;
using NPoco ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core ;
using Umbraco.Cms.Core.Cache ;
using Umbraco.Cms.Core.Models ;
using Umbraco.Cms.Core.Models.Entities ;
using Umbraco.Cms.Core.Persistence.Querying ;
2021-02-12 13:36:50 +01:00
using Umbraco.Cms.Core.Persistence.Repositories ;
2021-02-15 11:41:12 +01:00
using Umbraco.Cms.Core.Scoping ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core.Services ;
2021-02-12 13:36:50 +01:00
using Umbraco.Cms.Infrastructure.Persistence.Dtos ;
using Umbraco.Cms.Infrastructure.Persistence.Querying ;
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax ;
2021-02-09 11:26:22 +01:00
using Umbraco.Extensions ;
2021-02-09 10:22:42 +01:00
using static Umbraco . Cms . Core . Persistence . SqlExtensionsStatics ;
2017-12-07 16:45:25 +01:00
2021-02-12 13:36:50 +01:00
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
2017-12-07 16:45:25 +01:00
{
/// <summary>
2018-01-15 11:32:30 +01:00
/// Represents the EntityRepository used to query entity objects.
2017-12-07 16:45:25 +01:00
/// </summary>
/// <remarks>
2018-01-15 11:32:30 +01:00
/// <para>Limited to objects that have a corresponding node (in umbracoNode table).</para>
/// <para>Returns <see cref="IEntitySlim"/> objects, i.e. lightweight representation of entities.</para>
2017-12-07 16:45:25 +01:00
/// </remarks>
2020-12-09 22:43:49 +11:00
internal class EntityRepository : RepositoryBase , IEntityRepository
2017-12-07 16:45:25 +01:00
{
2020-12-09 22:43:49 +11:00
public EntityRepository ( IScopeAccessor scopeAccessor , AppCaches appCaches )
: base ( scopeAccessor , appCaches )
2017-12-07 16:45:25 +01:00
{
}
#region Repository
2020-12-09 22:43:49 +11:00
2018-01-15 11:32:30 +01:00
public IEnumerable < IEntitySlim > GetPagedResultsByQuery ( IQuery < IUmbracoEntity > query , Guid objectType , long pageIndex , int pageSize , out long totalRecords ,
2018-12-21 13:15:46 +11:00
IQuery < IUmbracoEntity > filter , Ordering ordering )
2017-12-07 16:45:25 +01:00
{
2019-11-05 15:05:51 +11:00
return GetPagedResultsByQuery ( query , new [ ] { objectType } , pageIndex , pageSize , out totalRecords , filter , ordering ) ;
}
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
// get a page of entities
public IEnumerable < IEntitySlim > GetPagedResultsByQuery ( IQuery < IUmbracoEntity > query , Guid [ ] objectTypes , long pageIndex , int pageSize , out long totalRecords ,
2019-11-15 14:14:09 +11:00
IQuery < IUmbracoEntity > filter , Ordering ordering , Action < Sql < ISqlContext > > sqlCustomization = null )
2019-11-05 15:05:51 +11:00
{
2021-02-09 10:22:42 +01:00
var isContent = objectTypes . Any ( objectType = > objectType = = Cms . Core . Constants . ObjectTypes . Document | | objectType = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ) ;
var isMedia = objectTypes . Any ( objectType = > objectType = = Cms . Core . Constants . ObjectTypes . Media ) ;
var isMember = objectTypes . Any ( objectType = > objectType = = Cms . Core . Constants . ObjectTypes . Member ) ;
2017-12-07 16:45:25 +01:00
2020-12-09 22:43:49 +11:00
Sql < ISqlContext > sql = GetBaseWhere ( isContent , isMedia , isMember , false , s = >
2017-12-07 16:45:25 +01:00
{
2019-11-15 14:14:09 +11:00
sqlCustomization ? . Invoke ( s ) ;
2019-11-06 12:43:10 +11:00
if ( filter ! = null )
{
2020-12-09 22:43:49 +11:00
foreach ( Tuple < string , object [ ] > filterClause in filter . GetWhereClauses ( ) )
{
2019-11-06 12:43:10 +11:00
s . Where ( filterClause . Item1 , filterClause . Item2 ) ;
2020-12-09 22:43:49 +11:00
}
2019-11-06 12:43:10 +11:00
}
2019-11-05 15:05:51 +11:00
} , objectTypes ) ;
2017-12-07 16:45:25 +01:00
2018-12-21 13:15:46 +11:00
ordering = ordering ? ? Ordering . ByDefault ( ) ;
2017-12-07 16:45:25 +01:00
var translator = new SqlTranslator < IUmbracoEntity > ( sql , query ) ;
sql = translator . Translate ( ) ;
2019-10-14 14:34:33 +02:00
sql = AddGroupBy ( isContent , isMedia , isMember , sql , ordering . IsEmpty ) ;
2018-12-21 13:15:46 +11:00
if ( ! ordering . IsEmpty )
{
// apply ordering
ApplyOrdering ( ref sql , ordering ) ;
}
2019-01-26 09:42:14 -05:00
// TODO: we should be able to do sql = sql.OrderBy(x => Alias(x.NodeId, "NodeId")); but we can't because the OrderBy extension don't support Alias currently
2020-12-09 22:43:49 +11:00
// no matter what we always must have node id ordered at the end
2018-12-21 13:15:46 +11:00
sql = ordering . Direction = = Direction . Ascending ? sql . OrderBy ( "NodeId" ) : sql . OrderByDescending ( "NodeId" ) ;
2017-12-07 16:45:25 +01:00
2019-02-09 15:41:30 +01:00
// for content we must query for ContentEntityDto entities to produce the correct culture variant entity names
var pageIndexToFetch = pageIndex + 1 ;
IEnumerable < BaseDto > dtos ;
2019-11-05 15:05:51 +11:00
var page = Database . Page < GenericContentEntityDto > ( pageIndexToFetch , pageSize , sql ) ;
dtos = page . Items ;
totalRecords = page . TotalItems ;
2019-02-09 15:41:30 +01:00
2019-11-05 15:05:51 +11:00
var entities = dtos . Select ( BuildEntity ) . ToArray ( ) ;
2019-02-08 08:50:57 +01:00
2019-11-05 15:05:51 +11:00
BuildVariants ( entities . OfType < DocumentEntitySlim > ( ) ) ;
2018-04-20 13:12:55 +10:00
2017-12-07 16:45:25 +01:00
return entities ;
}
2018-01-15 11:32:30 +01:00
public IEntitySlim Get ( Guid key )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBaseWhere ( false , false , false , false , key ) ;
2017-12-07 16:45:25 +01:00
var dto = Database . FirstOrDefault < BaseDto > ( sql ) ;
2019-11-05 15:05:51 +11:00
return dto = = null ? null : BuildEntity ( dto ) ;
2017-12-07 16:45:25 +01:00
}
2018-12-21 13:15:46 +11:00
2019-10-14 14:34:33 +02:00
private IEntitySlim GetEntity ( Sql < ISqlContext > sql , bool isContent , bool isMedia , bool isMember )
2017-12-07 16:45:25 +01:00
{
2020-12-09 22:43:49 +11:00
// isContent is going to return a 1:M result now with the variants so we need to do different things
2018-04-20 16:07:34 +10:00
if ( isContent )
{
2019-11-05 15:05:51 +11:00
var cdtos = Database . Fetch < DocumentEntityDto > ( sql ) ;
2018-09-25 10:55:33 +02:00
return cdtos . Count = = 0 ? null : BuildVariants ( BuildDocumentEntity ( cdtos [ 0 ] ) ) ;
2018-04-20 16:07:34 +10:00
}
2017-12-07 16:45:25 +01:00
2019-06-13 23:39:36 +10:00
var dto = isMedia
? Database . FirstOrDefault < MediaEntityDto > ( sql )
: Database . FirstOrDefault < BaseDto > ( sql ) ;
2018-04-24 17:59:26 +02:00
if ( dto = = null ) return null ;
2018-04-20 16:07:34 +10:00
2019-11-05 15:05:51 +11:00
var entity = BuildEntity ( dto ) ;
2017-12-07 16:45:25 +01:00
2018-04-24 17:59:26 +02:00
return entity ;
}
public IEntitySlim Get ( Guid key , Guid objectTypeId )
{
2021-02-09 10:22:42 +01:00
var isContent = objectTypeId = = Cms . Core . Constants . ObjectTypes . Document | | objectTypeId = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ;
var isMedia = objectTypeId = = Cms . Core . Constants . ObjectTypes . Media ;
var isMember = objectTypeId = = Cms . Core . Constants . ObjectTypes . Member ;
2018-04-24 17:59:26 +02:00
2019-10-14 14:34:33 +02:00
var sql = GetFullSqlForEntityType ( isContent , isMedia , isMember , objectTypeId , key ) ;
return GetEntity ( sql , isContent , isMedia , isMember ) ;
2017-12-07 16:45:25 +01:00
}
2018-11-19 17:54:36 +11:00
public IEntitySlim Get ( int id )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBaseWhere ( false , false , false , false , id ) ;
2017-12-07 16:45:25 +01:00
var dto = Database . FirstOrDefault < BaseDto > ( sql ) ;
2019-11-05 15:05:51 +11:00
return dto = = null ? null : BuildEntity ( dto ) ;
2017-12-07 16:45:25 +01:00
}
2018-11-19 17:54:36 +11:00
public IEntitySlim Get ( int id , Guid objectTypeId )
2017-12-07 16:45:25 +01:00
{
2021-02-09 10:22:42 +01:00
var isContent = objectTypeId = = Cms . Core . Constants . ObjectTypes . Document | | objectTypeId = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ;
var isMedia = objectTypeId = = Cms . Core . Constants . ObjectTypes . Media ;
var isMember = objectTypeId = = Cms . Core . Constants . ObjectTypes . Member ;
2017-12-07 16:45:25 +01:00
2019-10-14 14:34:33 +02:00
var sql = GetFullSqlForEntityType ( isContent , isMedia , isMember , objectTypeId , id ) ;
return GetEntity ( sql , isContent , isMedia , isMember ) ;
2017-12-07 16:45:25 +01:00
}
2018-11-19 17:54:36 +11:00
public IEnumerable < IEntitySlim > GetAll ( Guid objectType , params int [ ] ids )
2017-12-07 16:45:25 +01:00
{
return ids . Length > 0
? PerformGetAll ( objectType , sql = > sql . WhereIn < NodeDto > ( x = > x . NodeId , ids . Distinct ( ) ) )
: PerformGetAll ( objectType ) ;
}
2018-11-19 17:54:36 +11:00
public IEnumerable < IEntitySlim > GetAll ( Guid objectType , params Guid [ ] keys )
2017-12-07 16:45:25 +01:00
{
return keys . Length > 0
? PerformGetAll ( objectType , sql = > sql . WhereIn < NodeDto > ( x = > x . UniqueId , keys . Distinct ( ) ) )
: PerformGetAll ( objectType ) ;
}
2019-10-14 14:34:33 +02:00
private IEnumerable < IEntitySlim > GetEntities ( Sql < ISqlContext > sql , bool isContent , bool isMedia , bool isMember )
2017-12-07 16:45:25 +01:00
{
2020-12-09 22:43:49 +11:00
// isContent is going to return a 1:M result now with the variants so we need to do different things
2018-04-20 16:07:34 +10:00
if ( isContent )
{
2019-11-05 15:05:51 +11:00
var cdtos = Database . Fetch < DocumentEntityDto > ( sql ) ;
2018-09-21 15:49:37 +10:00
2018-04-24 17:59:26 +02:00
return cdtos . Count = = 0
? Enumerable . Empty < IEntitySlim > ( )
2018-09-25 10:55:33 +02:00
: BuildVariants ( cdtos . Select ( BuildDocumentEntity ) ) . ToList ( ) ;
2018-04-20 16:07:34 +10:00
}
2017-12-07 16:45:25 +01:00
2019-06-13 23:39:36 +10:00
var dtos = isMedia
? ( IEnumerable < BaseDto > ) Database . Fetch < MediaEntityDto > ( sql )
: Database . Fetch < BaseDto > ( sql ) ;
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
var entities = dtos . Select ( BuildEntity ) . ToArray ( ) ;
2018-04-20 16:07:34 +10:00
2018-04-24 17:59:26 +02:00
return entities ;
}
private IEnumerable < IEntitySlim > PerformGetAll ( Guid objectType , Action < Sql < ISqlContext > > filter = null )
{
2021-02-09 10:22:42 +01:00
var isContent = objectType = = Cms . Core . Constants . ObjectTypes . Document | | objectType = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ;
var isMedia = objectType = = Cms . Core . Constants . ObjectTypes . Media ;
var isMember = objectType = = Cms . Core . Constants . ObjectTypes . Member ;
2018-04-24 17:59:26 +02:00
2019-10-14 14:34:33 +02:00
var sql = GetFullSqlForEntityType ( isContent , isMedia , isMember , objectType , filter ) ;
return GetEntities ( sql , isContent , isMedia , isMember ) ;
2017-12-07 16:45:25 +01:00
}
2018-11-19 17:54:36 +11:00
public IEnumerable < TreeEntityPath > GetAllPaths ( Guid objectType , params int [ ] ids )
2017-12-07 16:45:25 +01:00
{
return ids . Any ( )
? PerformGetAllPaths ( objectType , sql = > sql . WhereIn < NodeDto > ( x = > x . NodeId , ids . Distinct ( ) ) )
: PerformGetAllPaths ( objectType ) ;
}
2018-11-19 17:54:36 +11:00
public IEnumerable < TreeEntityPath > GetAllPaths ( Guid objectType , params Guid [ ] keys )
2017-12-07 16:45:25 +01:00
{
return keys . Any ( )
? PerformGetAllPaths ( objectType , sql = > sql . WhereIn < NodeDto > ( x = > x . UniqueId , keys . Distinct ( ) ) )
: PerformGetAllPaths ( objectType ) ;
}
2018-01-10 12:48:51 +01:00
private IEnumerable < TreeEntityPath > PerformGetAllPaths ( Guid objectType , Action < Sql < ISqlContext > > filter = null )
2017-12-07 16:45:25 +01:00
{
2019-02-08 08:50:57 +01:00
// NodeId is named Id on TreeEntityPath = use an alias
2019-02-08 12:42:46 +01:00
var sql = Sql ( ) . Select < NodeDto > ( x = > Alias ( x . NodeId , nameof ( TreeEntityPath . Id ) ) , x = > x . Path ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . NodeObjectType = = objectType ) ;
2017-12-07 16:45:25 +01:00
filter ? . Invoke ( sql ) ;
2018-01-10 12:48:51 +01:00
return Database . Fetch < TreeEntityPath > ( sql ) ;
2017-12-07 16:45:25 +01:00
}
2018-11-19 17:54:36 +11:00
public IEnumerable < IEntitySlim > GetByQuery ( IQuery < IUmbracoEntity > query )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sqlClause = GetBase ( false , false , false , null ) ;
2017-12-07 16:45:25 +01:00
var translator = new SqlTranslator < IUmbracoEntity > ( sqlClause , query ) ;
var sql = translator . Translate ( ) ;
2019-10-14 14:34:33 +02:00
sql = AddGroupBy ( false , false , false , sql , true ) ;
2017-12-07 16:45:25 +01:00
var dtos = Database . Fetch < BaseDto > ( sql ) ;
2019-11-05 15:05:51 +11:00
return dtos . Select ( BuildEntity ) . ToList ( ) ;
2017-12-07 16:45:25 +01:00
}
2018-11-19 17:54:36 +11:00
public IEnumerable < IEntitySlim > GetByQuery ( IQuery < IUmbracoEntity > query , Guid objectType )
2017-12-07 16:45:25 +01:00
{
2021-02-09 10:22:42 +01:00
var isContent = objectType = = Cms . Core . Constants . ObjectTypes . Document | | objectType = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ;
var isMedia = objectType = = Cms . Core . Constants . ObjectTypes . Media ;
var isMember = objectType = = Cms . Core . Constants . ObjectTypes . Member ;
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
var sql = GetBaseWhere ( isContent , isMedia , isMember , false , null , new [ ] { objectType } ) ;
2018-04-20 13:12:55 +10:00
2017-12-07 16:45:25 +01:00
var translator = new SqlTranslator < IUmbracoEntity > ( sql , query ) ;
sql = translator . Translate ( ) ;
2019-10-14 14:34:33 +02:00
sql = AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
2019-10-14 14:34:33 +02:00
return GetEntities ( sql , isContent , isMedia , isMember ) ;
2017-12-07 16:45:25 +01:00
}
public UmbracoObjectTypes GetObjectType ( int id )
{
var sql = Sql ( ) . Select < NodeDto > ( x = > x . NodeObjectType ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . NodeId = = id ) ;
2018-01-15 11:32:30 +01:00
return ObjectTypes . GetUmbracoObjectType ( Database . ExecuteScalar < Guid > ( sql ) ) ;
2017-12-07 16:45:25 +01:00
}
public UmbracoObjectTypes GetObjectType ( Guid key )
{
var sql = Sql ( ) . Select < NodeDto > ( x = > x . NodeObjectType ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . UniqueId = = key ) ;
2018-01-15 11:32:30 +01:00
return ObjectTypes . GetUmbracoObjectType ( Database . ExecuteScalar < Guid > ( sql ) ) ;
2017-12-07 16:45:25 +01:00
}
public bool Exists ( Guid key )
{
var sql = Sql ( ) . SelectCount ( ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . UniqueId = = key ) ;
return Database . ExecuteScalar < int > ( sql ) > 0 ;
}
public bool Exists ( int id )
{
var sql = Sql ( ) . SelectCount ( ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . NodeId = = id ) ;
return Database . ExecuteScalar < int > ( sql ) > 0 ;
}
2018-09-25 10:55:33 +02:00
private DocumentEntitySlim BuildVariants ( DocumentEntitySlim entity )
= > BuildVariants ( new [ ] { entity } ) . First ( ) ;
2018-09-21 15:49:37 +10:00
2018-09-25 10:55:33 +02:00
private IEnumerable < DocumentEntitySlim > BuildVariants ( IEnumerable < DocumentEntitySlim > entities )
2018-09-21 15:49:37 +10:00
{
2018-09-25 10:55:33 +02:00
List < DocumentEntitySlim > v = null ;
var entitiesList = entities . ToList ( ) ;
foreach ( var e in entitiesList )
2018-09-21 15:49:37 +10:00
{
2018-09-25 10:55:33 +02:00
if ( e . Variations . VariesByCulture ( ) )
( v ? ? ( v = new List < DocumentEntitySlim > ( ) ) ) . Add ( e ) ;
}
2018-09-21 15:49:37 +10:00
2018-09-25 10:55:33 +02:00
if ( v = = null ) return entitiesList ;
2018-09-21 15:49:37 +10:00
2018-09-25 10:55:33 +02:00
// fetch all variant info dtos
2021-10-13 13:42:18 +02:00
var dtos = Database . FetchByGroups < VariantInfoDto , int > ( v . Select ( x = > x . Id ) , Constants . Sql . MaxParameterCount , GetVariantInfos ) ;
2018-09-25 10:55:33 +02:00
// group by node id (each group contains all languages)
var xdtos = dtos . GroupBy ( x = > x . NodeId ) . ToDictionary ( x = > x . Key , x = > x ) ;
foreach ( var e in v )
{
// since we're only iterating on entities that vary, we must have something
var edtos = xdtos [ e . Id ] ;
e . CultureNames = edtos . Where ( x = > x . CultureAvailable ) . ToDictionary ( x = > x . IsoCode , x = > x . Name ) ;
e . PublishedCultures = edtos . Where ( x = > x . CulturePublished ) . Select ( x = > x . IsoCode ) ;
e . EditedCultures = edtos . Where ( x = > x . CultureAvailable & & x . CultureEdited ) . Select ( x = > x . IsoCode ) ;
2018-09-21 15:49:37 +10:00
}
2018-09-25 10:55:33 +02:00
return entitiesList ;
2018-09-21 15:49:37 +10:00
}
2017-12-07 16:45:25 +01:00
#endregion
#region Sql
2018-09-25 10:55:33 +02:00
protected Sql < ISqlContext > GetVariantInfos ( IEnumerable < int > ids )
2018-09-21 15:49:37 +10:00
{
2018-09-25 10:55:33 +02:00
return Sql ( )
. Select < NodeDto > ( x = > x . NodeId )
2018-09-21 15:49:37 +10:00
. AndSelect < LanguageDto > ( x = > x . IsoCode )
2018-09-25 10:55:33 +02:00
. AndSelect < DocumentDto > ( "doc" , x = > Alias ( x . Published , "DocumentPublished" ) , x = > Alias ( x . Edited , "DocumentEdited" ) )
2018-09-25 16:55:00 +02:00
. AndSelect < DocumentCultureVariationDto > ( "dcv" ,
x = > Alias ( x . Available , "CultureAvailable" ) , x = > Alias ( x . Published , "CulturePublished" ) , x = > Alias ( x . Edited , "CultureEdited" ) ,
x = > Alias ( x . Name , "Name" ) )
2018-09-25 10:55:33 +02:00
// from node x language
2018-09-21 15:49:37 +10:00
. From < NodeDto > ( )
2018-09-25 10:55:33 +02:00
. CrossJoin < LanguageDto > ( )
// join to document - always exists - indicates global document published/edited status
. InnerJoin < DocumentDto > ( "doc" )
. On < NodeDto , DocumentDto > ( ( node , doc ) = > node . NodeId = = doc . NodeId , aliasRight : "doc" )
// left-join do document variation - matches cultures that are *available* + indicates when *edited*
. LeftJoin < DocumentCultureVariationDto > ( "dcv" )
. On < NodeDto , DocumentCultureVariationDto , LanguageDto > ( ( node , dcv , lang ) = > node . NodeId = = dcv . NodeId & & lang . Id = = dcv . LanguageId , aliasRight : "dcv" )
// for selected nodes
. WhereIn < NodeDto > ( x = > x . NodeId , ids ) ;
2018-09-21 15:49:37 +10:00
}
2018-09-25 10:55:33 +02:00
2017-12-07 16:45:25 +01:00
// gets the full sql for a given object type and a given unique id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetFullSqlForEntityType ( bool isContent , bool isMedia , bool isMember , Guid objectType , Guid uniqueId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBaseWhere ( isContent , isMedia , isMember , false , objectType , uniqueId ) ;
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the full sql for a given object type and a given node id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetFullSqlForEntityType ( bool isContent , bool isMedia , bool isMember , Guid objectType , int nodeId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBaseWhere ( isContent , isMedia , isMember , false , objectType , nodeId ) ;
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the full sql for a given object type, with a given filter
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetFullSqlForEntityType ( bool isContent , bool isMedia , bool isMember , Guid objectType , Action < Sql < ISqlContext > > filter )
2017-12-07 16:45:25 +01:00
{
2019-11-05 15:05:51 +11:00
var sql = GetBaseWhere ( isContent , isMedia , isMember , false , filter , new [ ] { objectType } ) ;
2019-10-14 14:34:33 +02:00
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the base SELECT + FROM [+ filter] sql
// always from the 'current' content version
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetBase ( bool isContent , bool isMedia , bool isMember , Action < Sql < ISqlContext > > filter , bool isCount = false )
2019-11-05 15:05:51 +11:00
{
2017-12-07 16:45:25 +01:00
var sql = Sql ( ) ;
if ( isCount )
{
sql . SelectCount ( ) ;
}
else
{
sql
. Select < NodeDto > ( x = > x . NodeId , x = > x . Trashed , x = > x . ParentId , x = > x . UserId , x = > x . Level , x = > x . Path )
. AndSelect < NodeDto > ( x = > x . SortOrder , x = > x . UniqueId , x = > x . Text , x = > x . NodeObjectType , x = > x . CreateDate )
. Append ( ", COUNT(child.id) AS children" ) ;
2019-10-14 14:34:33 +02:00
if ( isContent | | isMedia | | isMember )
2017-12-07 16:45:25 +01:00
sql
2020-08-26 08:05:15 +02:00
. AndSelect < ContentVersionDto > ( x = > Alias ( x . Id , "versionId" ) , x = > x . VersionDate )
2018-05-07 18:22:23 +02:00
. AndSelect < ContentTypeDto > ( x = > x . Alias , x = > x . Icon , x = > x . Thumbnail , x = > x . IsContainer , x = > x . Variations ) ;
2018-04-20 13:12:55 +10:00
if ( isContent )
{
sql
2018-09-25 10:55:33 +02:00
. AndSelect < DocumentDto > ( x = > x . Published , x = > x . Edited ) ;
2018-04-20 13:12:55 +10:00
}
2019-06-13 23:39:36 +10:00
if ( isMedia )
{
sql
. AndSelect < MediaVersionDto > ( x = > Alias ( x . Path , "MediaPath" ) ) ;
}
2017-12-07 16:45:25 +01:00
}
sql
. From < NodeDto > ( ) ;
2019-10-14 14:34:33 +02:00
if ( isContent | | isMedia | | isMember )
2017-12-07 16:45:25 +01:00
{
sql
2019-11-05 15:05:51 +11:00
. LeftJoin < ContentVersionDto > ( ) . On < NodeDto , ContentVersionDto > ( ( left , right ) = > left . NodeId = = right . NodeId & & right . Current )
. LeftJoin < ContentDto > ( ) . On < NodeDto , ContentDto > ( ( left , right ) = > left . NodeId = = right . NodeId )
. LeftJoin < ContentTypeDto > ( ) . On < ContentDto , ContentTypeDto > ( ( left , right ) = > left . ContentTypeId = = right . NodeId ) ;
2017-12-07 16:45:25 +01:00
}
if ( isContent )
{
sql
2019-11-05 15:05:51 +11:00
. LeftJoin < DocumentDto > ( ) . On < NodeDto , DocumentDto > ( ( left , right ) = > left . NodeId = = right . NodeId ) ;
2017-12-07 16:45:25 +01:00
}
2019-06-13 23:39:36 +10:00
if ( isMedia )
{
sql
2019-08-24 17:29:37 +01:00
. LeftJoin < MediaVersionDto > ( ) . On < ContentVersionDto , MediaVersionDto > ( ( left , right ) = > left . Id = = right . Id ) ;
2019-06-13 23:39:36 +10:00
}
2018-04-20 13:12:55 +10:00
//Any LeftJoin statements need to come last
2017-12-07 16:45:25 +01:00
if ( isCount = = false )
2018-04-20 13:12:55 +10:00
{
2017-12-07 16:45:25 +01:00
sql
. LeftJoin < NodeDto > ( "child" ) . On < NodeDto , NodeDto > ( ( left , right ) = > left . NodeId = = right . ParentId , aliasRight : "child" ) ;
2018-04-20 13:12:55 +10:00
}
2017-12-07 16:45:25 +01:00
filter ? . Invoke ( sql ) ;
return sql ;
}
// gets the base SELECT + FROM [+ filter] + WHERE sql
// for a given object type, with a given filter
2019-11-05 15:05:51 +11:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , Action < Sql < ISqlContext > > filter , Guid [ ] objectTypes )
2017-12-07 16:45:25 +01:00
{
2019-11-06 12:43:10 +11:00
var sql = GetBase ( isContent , isMedia , isMember , filter , isCount ) ;
if ( objectTypes . Length > 0 )
{
sql . WhereIn < NodeDto > ( x = > x . NodeObjectType , objectTypes ) ;
}
return sql ;
2017-12-07 16:45:25 +01:00
}
// gets the base SELECT + FROM + WHERE sql
// for a given node id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , int id )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBase ( isContent , isMedia , isMember , null , isCount )
2017-12-07 16:45:25 +01:00
. Where < NodeDto > ( x = > x . NodeId = = id ) ;
2019-10-14 14:34:33 +02:00
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the base SELECT + FROM + WHERE sql
// for a given unique id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , Guid uniqueId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBase ( isContent , isMedia , isMember , null , isCount )
2017-12-07 16:45:25 +01:00
. Where < NodeDto > ( x = > x . UniqueId = = uniqueId ) ;
2019-10-14 14:34:33 +02:00
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the base SELECT + FROM + WHERE sql
// for a given object type and node id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , Guid objectType , int nodeId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
return GetBase ( isContent , isMedia , isMember , null , isCount )
2017-12-07 16:45:25 +01:00
. Where < NodeDto > ( x = > x . NodeId = = nodeId & & x . NodeObjectType = = objectType ) ;
}
// gets the base SELECT + FROM + WHERE sql
// for a given object type and unique id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , Guid objectType , Guid uniqueId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
return GetBase ( isContent , isMedia , isMember , null , isCount )
2017-12-07 16:45:25 +01:00
. Where < NodeDto > ( x = > x . UniqueId = = uniqueId & & x . NodeObjectType = = objectType ) ;
}
// gets the GROUP BY / ORDER BY sql
// required in order to count children
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > AddGroupBy ( bool isContent , bool isMedia , bool isMember , Sql < ISqlContext > sql , bool defaultSort )
2017-12-07 16:45:25 +01:00
{
sql
. GroupBy < NodeDto > ( x = > x . NodeId , x = > x . Trashed , x = > x . ParentId , x = > x . UserId , x = > x . Level , x = > x . Path )
. AndBy < NodeDto > ( x = > x . SortOrder , x = > x . UniqueId , x = > x . Text , x = > x . NodeObjectType , x = > x . CreateDate ) ;
if ( isContent )
2018-04-20 13:12:55 +10:00
{
2017-12-07 16:45:25 +01:00
sql
2018-09-25 10:55:33 +02:00
. AndBy < DocumentDto > ( x = > x . Published , x = > x . Edited ) ;
2018-04-20 13:12:55 +10:00
}
2019-06-13 23:39:36 +10:00
if ( isMedia )
{
sql
. AndBy < MediaVersionDto > ( x = > Alias ( x . Path , "MediaPath" ) ) ;
}
2017-12-07 16:45:25 +01:00
2019-10-14 14:34:33 +02:00
if ( isContent | | isMedia | | isMember )
2017-12-07 16:45:25 +01:00
sql
2020-08-26 08:05:15 +02:00
. AndBy < ContentVersionDto > ( x = > x . Id , x = > x . VersionDate )
2018-05-07 18:22:23 +02:00
. AndBy < ContentTypeDto > ( x = > x . Alias , x = > x . Icon , x = > x . Thumbnail , x = > x . IsContainer , x = > x . Variations ) ;
2017-12-07 16:45:25 +01:00
2018-12-21 13:15:46 +11:00
if ( defaultSort )
2017-12-07 16:45:25 +01:00
sql . OrderBy < NodeDto > ( x = > x . SortOrder ) ;
return sql ;
}
2018-12-21 13:15:46 +11:00
private void ApplyOrdering ( ref Sql < ISqlContext > sql , Ordering ordering )
{
if ( sql = = null ) throw new ArgumentNullException ( nameof ( sql ) ) ;
if ( ordering = = null ) throw new ArgumentNullException ( nameof ( ordering ) ) ;
2019-11-05 15:05:51 +11:00
// TODO: although the default ordering string works for name, it wont work for others without a table or an alias of some sort
// As more things are attempted to be sorted we'll prob have to add more expressions here
2019-11-11 12:05:26 +00:00
string orderBy ;
switch ( ordering . OrderBy . ToUpperInvariant ( ) )
2019-11-05 15:05:51 +11:00
{
2019-11-11 12:05:26 +00:00
case "PATH" :
orderBy = SqlSyntax . GetQuotedColumn ( NodeDto . TableName , "path" ) ;
break ;
default :
orderBy = ordering . OrderBy ;
break ;
2021-02-09 10:22:42 +01:00
}
2018-12-21 13:15:46 +11:00
if ( ordering . Direction = = Direction . Ascending )
sql . OrderBy ( orderBy ) ;
else
sql . OrderByDescending ( orderBy ) ;
}
2017-12-07 16:45:25 +01:00
#endregion
#region Classes
/// <summary>
2019-11-05 15:05:51 +11:00
/// The DTO used to fetch results for a generic content item which could be either a document, media or a member
2017-12-07 16:45:25 +01:00
/// </summary>
2019-11-05 15:05:51 +11:00
private class GenericContentEntityDto : DocumentEntityDto
{
public string MediaPath { get ; set ; }
}
/// <summary>
/// The DTO used to fetch results for a document item with its variation info
2017-12-07 16:45:25 +01:00
/// </summary>
2019-11-05 15:05:51 +11:00
private class DocumentEntityDto : BaseDto
2018-09-21 15:49:37 +10:00
{
public ContentVariation Variations { get ; set ; }
public bool Published { get ; set ; }
public bool Edited { get ; set ; }
}
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
/// <summary>
/// The DTO used to fetch results for a media item with its media path info
/// </summary>
2019-06-13 23:39:36 +10:00
private class MediaEntityDto : BaseDto
{
public string MediaPath { get ; set ; }
}
2019-11-05 15:05:51 +11:00
/// <summary>
/// The DTO used to fetch results for a member item
/// </summary>
2019-10-14 14:34:33 +02:00
private class MemberEntityDto : BaseDto
{
}
2018-09-25 10:55:33 +02:00
public class VariantInfoDto
2018-09-21 15:49:37 +10:00
{
public int NodeId { get ; set ; }
public string IsoCode { get ; set ; }
public string Name { get ; set ; }
2018-09-25 10:55:33 +02:00
public bool DocumentPublished { get ; set ; }
public bool DocumentEdited { get ; set ; }
2018-09-25 16:55:00 +02:00
public bool CultureAvailable { get ; set ; }
public bool CulturePublished { get ; set ; }
public bool CultureEdited { get ; set ; }
2018-09-21 15:49:37 +10:00
}
// ReSharper disable once ClassNeverInstantiated.Local
/// <summary>
/// the DTO corresponding to fields selected by GetBase
/// </summary>
private class BaseDto
{
// ReSharper disable UnusedAutoPropertyAccessor.Local
// ReSharper disable UnusedMember.Local
public int NodeId { get ; set ; }
public bool Trashed { get ; set ; }
public int ParentId { get ; set ; }
public int? UserId { get ; set ; }
public int Level { get ; set ; }
public string Path { get ; set ; }
public int SortOrder { get ; set ; }
public Guid UniqueId { get ; set ; }
public string Text { get ; set ; }
public Guid NodeObjectType { get ; set ; }
public DateTime CreateDate { get ; set ; }
2020-08-26 08:05:15 +02:00
public DateTime VersionDate { get ; set ; }
2018-09-21 15:49:37 +10:00
public int Children { get ; set ; }
public int VersionId { get ; set ; }
public string Alias { get ; set ; }
public string Icon { get ; set ; }
public string Thumbnail { get ; set ; }
public bool IsContainer { get ; set ; }
// ReSharper restore UnusedAutoPropertyAccessor.Local
// ReSharper restore UnusedMember.Local
}
2017-12-07 16:45:25 +01:00
#endregion
#region Factory
2019-11-05 15:05:51 +11:00
private EntitySlim BuildEntity ( BaseDto dto )
2018-01-12 10:48:36 +01:00
{
2021-02-09 10:22:42 +01:00
if ( dto . NodeObjectType = = Cms . Core . Constants . ObjectTypes . Document )
2018-01-12 10:48:36 +01:00
return BuildDocumentEntity ( dto ) ;
2021-02-09 10:22:42 +01:00
if ( dto . NodeObjectType = = Cms . Core . Constants . ObjectTypes . Media )
2019-06-13 23:39:36 +10:00
return BuildMediaEntity ( dto ) ;
2021-02-09 10:22:42 +01:00
if ( dto . NodeObjectType = = Cms . Core . Constants . ObjectTypes . Member )
2019-10-14 14:34:33 +02:00
return BuildMemberEntity ( dto ) ;
2017-12-07 16:45:25 +01:00
2018-01-15 11:32:30 +01:00
// EntitySlim does not track changes
var entity = new EntitySlim ( ) ;
BuildEntity ( entity , dto ) ;
2018-01-12 10:48:36 +01:00
return entity ;
}
2017-12-07 16:45:25 +01:00
2018-01-15 11:32:30 +01:00
private static void BuildEntity ( EntitySlim entity , BaseDto dto )
2017-12-07 16:45:25 +01:00
{
2018-01-12 10:48:36 +01:00
entity . Trashed = dto . Trashed ;
entity . CreateDate = dto . CreateDate ;
2020-08-26 08:05:15 +02:00
entity . UpdateDate = dto . VersionDate ;
2021-02-09 10:22:42 +01:00
entity . CreatorId = dto . UserId ? ? Cms . Core . Constants . Security . UnknownUserId ;
2018-01-12 10:48:36 +01:00
entity . Id = dto . NodeId ;
entity . Key = dto . UniqueId ;
entity . Level = dto . Level ;
entity . Name = dto . Text ;
entity . NodeObjectType = dto . NodeObjectType ;
entity . ParentId = dto . ParentId ;
entity . Path = dto . Path ;
entity . SortOrder = dto . SortOrder ;
entity . HasChildren = dto . Children > 0 ;
2018-01-15 11:32:30 +01:00
entity . IsContainer = dto . IsContainer ;
2018-01-12 10:48:36 +01:00
}
2018-01-15 11:32:30 +01:00
private static void BuildContentEntity ( ContentEntitySlim entity , BaseDto dto )
2018-01-12 10:48:36 +01:00
{
BuildEntity ( entity , dto ) ;
entity . ContentTypeAlias = dto . Alias ;
entity . ContentTypeIcon = dto . Icon ;
entity . ContentTypeThumbnail = dto . Thumbnail ;
}
2019-06-13 23:39:36 +10:00
private MediaEntitySlim BuildMediaEntity ( BaseDto dto )
2018-01-12 10:48:36 +01:00
{
2018-01-15 11:32:30 +01:00
// EntitySlim does not track changes
2019-06-13 23:39:36 +10:00
var entity = new MediaEntitySlim ( ) ;
2018-01-15 11:32:30 +01:00
BuildContentEntity ( entity , dto ) ;
2019-06-13 23:39:36 +10:00
2020-03-01 20:07:49 +01:00
// fill in the media info
if ( dto is MediaEntityDto mediaEntityDto )
2019-06-13 23:39:36 +10:00
{
2020-03-01 20:07:49 +01:00
entity . MediaPath = mediaEntityDto . MediaPath ;
}
else if ( dto is GenericContentEntityDto genericContentEntityDto )
{
entity . MediaPath = genericContentEntityDto . MediaPath ;
2019-06-13 23:39:36 +10:00
}
2018-01-12 10:48:36 +01:00
return entity ;
}
2017-12-07 16:45:25 +01:00
2018-05-07 23:22:52 +10:00
private DocumentEntitySlim BuildDocumentEntity ( BaseDto dto )
2018-01-12 10:48:36 +01:00
{
2018-01-15 11:32:30 +01:00
// EntitySlim does not track changes
var entity = new DocumentEntitySlim ( ) ;
2018-05-08 12:43:07 +10:00
BuildContentEntity ( entity , dto ) ;
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
if ( dto is DocumentEntityDto contentDto )
2018-04-20 13:12:55 +10:00
{
2018-09-25 10:55:33 +02:00
// fill in the invariant info
entity . Edited = contentDto . Edited ;
entity . Published = contentDto . Published ;
entity . Variations = contentDto . Variations ;
2018-04-20 13:12:55 +10:00
}
2018-09-25 10:55:33 +02:00
2018-04-20 13:12:55 +10:00
return entity ;
}
2019-10-14 14:34:33 +02:00
private MemberEntitySlim BuildMemberEntity ( BaseDto dto )
{
// EntitySlim does not track changes
var entity = new MemberEntitySlim ( ) ;
BuildEntity ( entity , dto ) ;
entity . ContentTypeAlias = dto . Alias ;
entity . ContentTypeIcon = dto . Icon ;
entity . ContentTypeThumbnail = dto . Thumbnail ;
return entity ;
}
2017-12-07 16:45:25 +01:00
#endregion
}
}