2017-07-20 11:21:28 +02:00
using System ;
2015-11-10 12:30:22 +01:00
using System.Collections.Generic ;
using System.Linq ;
2020-09-17 09:42:55 +02:00
using Microsoft.Extensions.Logging ;
2016-04-12 15:11:07 +02:00
using NPoco ;
2015-11-10 16:14:03 +01:00
using Umbraco.Core.Cache ;
2015-11-10 12:30:22 +01:00
using Umbraco.Core.Models ;
2017-12-28 09:06:33 +01:00
using Umbraco.Core.Persistence.Dtos ;
2015-11-10 12:30:22 +01:00
using Umbraco.Core.Persistence.Querying ;
2017-12-12 15:04:13 +01:00
using Umbraco.Core.Scoping ;
2015-11-10 12:30:22 +01:00
2017-12-07 16:45:25 +01:00
namespace Umbraco.Core.Persistence.Repositories.Implement
2015-11-10 12:30:22 +01:00
{
/// <summary>
2015-11-20 13:39:42 +01:00
/// An internal repository for managing entity containers such as doc type, media type, data type containers.
2015-11-10 12:30:22 +01:00
/// </summary>
2017-12-14 17:04:44 +01:00
internal class EntityContainerRepository : NPocoRepositoryBase < int , EntityContainer > , IEntityContainerRepository
2015-11-10 12:30:22 +01:00
{
2016-01-14 19:33:53 +01:00
private readonly Guid _containerObjectType ;
2020-09-17 09:42:55 +02:00
public EntityContainerRepository ( IScopeAccessor scopeAccessor , AppCaches cache , ILogger < EntityContainerRepository > logger , Guid containerObjectType )
2017-12-14 17:04:44 +01:00
: base ( scopeAccessor , cache , logger )
2016-01-14 19:33:53 +01:00
{
2017-09-19 18:19:05 +02:00
var allowedContainers = new [ ] { Constants . ObjectTypes . DocumentTypeContainer , Constants . ObjectTypes . MediaTypeContainer , Constants . ObjectTypes . DataTypeContainer } ;
2016-01-14 19:33:53 +01:00
_containerObjectType = containerObjectType ;
if ( allowedContainers . Contains ( _containerObjectType ) = = false )
throw new InvalidOperationException ( "No container type exists with ID: " + _containerObjectType ) ;
}
2015-11-10 12:30:22 +01:00
2017-05-12 14:49:44 +02:00
// never cache
2017-12-15 16:29:14 +01:00
protected override IRepositoryCachePolicy < EntityContainer , int > CreateCachePolicy ( )
2015-11-10 16:14:03 +01:00
{
2017-12-15 16:29:14 +01:00
return NoCacheRepositoryCachePolicy < EntityContainer , int > . Instance ;
2015-11-10 16:14:03 +01:00
}
2015-11-10 12:30:22 +01:00
protected override EntityContainer PerformGet ( int id )
{
2016-01-14 19:33:53 +01:00
var sql = GetBaseQuery ( false ) . Where ( GetBaseWhereClause ( ) , new { id = id , NodeObjectType = NodeObjectTypeId } ) ;
2015-11-10 16:14:03 +01:00
2016-08-02 12:11:35 +02:00
var nodeDto = Database . Fetch < NodeDto > ( SqlSyntax . SelectTop ( sql , 1 ) ) . FirstOrDefault ( ) ;
2015-11-20 13:39:42 +01:00
return nodeDto = = null ? null : CreateEntity ( nodeDto ) ;
}
2015-11-10 16:14:03 +01:00
2015-11-20 13:39:42 +01:00
// temp - so we don't have to implement GetByQuery
public EntityContainer Get ( Guid id )
{
var sql = GetBaseQuery ( false ) . Where ( "UniqueId=@uniqueId" , new { uniqueId = id } ) ;
2015-11-10 16:14:03 +01:00
2015-11-20 13:39:42 +01:00
var nodeDto = Database . Fetch < NodeDto > ( sql ) . FirstOrDefault ( ) ;
return nodeDto = = null ? null : CreateEntity ( nodeDto ) ;
2015-11-10 12:30:22 +01:00
}
2016-01-14 19:33:53 +01:00
public IEnumerable < EntityContainer > Get ( string name , int level )
2016-01-11 17:52:29 +01:00
{
2016-01-14 19:33:53 +01:00
var sql = GetBaseQuery ( false ) . Where ( "text=@name AND level=@level AND nodeObjectType=@umbracoObjectTypeId" , new { name , level , umbracoObjectTypeId = NodeObjectTypeId } ) ;
2016-01-12 14:32:45 +01:00
return Database . Fetch < NodeDto > ( sql ) . Select ( CreateEntity ) ;
2015-11-10 12:30:22 +01:00
}
protected override IEnumerable < EntityContainer > PerformGetAll ( params int [ ] ids )
{
2017-05-12 14:49:44 +02:00
if ( ids . Any ( ) )
2016-01-14 19:33:53 +01:00
{
2017-05-12 14:49:44 +02:00
return Database . FetchByGroups < NodeDto , int > ( ids , 2000 , batch = >
GetBaseQuery ( false )
. Where < NodeDto > ( x = > x . NodeObjectType = = NodeObjectTypeId )
. WhereIn < NodeDto > ( x = > x . NodeId , batch ) )
. Select ( CreateEntity ) ;
}
2016-01-14 19:33:53 +01:00
2017-05-12 14:49:44 +02:00
// else
2016-01-14 19:33:53 +01:00
2017-05-12 14:49:44 +02:00
var sql = GetBaseQuery ( false )
. Where ( "nodeObjectType=@umbracoObjectTypeId" , new { umbracoObjectTypeId = NodeObjectTypeId } )
. OrderBy < NodeDto > ( x = > x . Level ) ;
return Database . Fetch < NodeDto > ( sql ) . Select ( CreateEntity ) ;
2015-11-10 12:30:22 +01:00
}
protected override IEnumerable < EntityContainer > PerformGetByQuery ( IQuery < EntityContainer > query )
{
throw new NotImplementedException ( ) ;
}
2015-11-20 13:39:42 +01:00
private static EntityContainer CreateEntity ( NodeDto nodeDto )
{
if ( nodeDto . NodeObjectType . HasValue = = false )
throw new InvalidOperationException ( "Node with id " + nodeDto . NodeId + " has no object type." ) ;
// throws if node is not a container
var containedObjectType = EntityContainer . GetContainedObjectType ( nodeDto . NodeObjectType . Value ) ;
var entity = new EntityContainer ( nodeDto . NodeId , nodeDto . UniqueId ,
nodeDto . ParentId , nodeDto . Path , nodeDto . Level , nodeDto . SortOrder ,
containedObjectType ,
2018-05-31 23:05:35 +10:00
nodeDto . Text , nodeDto . UserId ? ? Constants . Security . UnknownUserId ) ;
2015-11-20 13:39:42 +01:00
2017-11-10 11:27:12 +01:00
// reset dirty initial properties (U4-1946)
2015-11-20 13:39:42 +01:00
entity . ResetDirtyProperties ( false ) ;
return entity ;
}
2017-09-22 18:48:58 +02:00
protected override Sql < ISqlContext > GetBaseQuery ( bool isCount )
2015-11-10 12:30:22 +01:00
{
2016-04-12 15:11:07 +02:00
var sql = Sql ( ) ;
2015-11-10 16:14:03 +01:00
if ( isCount )
2016-04-12 15:11:07 +02:00
sql . SelectCount ( ) ;
2015-11-10 16:14:03 +01:00
else
2016-04-12 15:11:07 +02:00
sql . SelectAll ( ) ;
sql . From < NodeDto > ( ) ;
2015-11-10 16:14:03 +01:00
return sql ;
2015-11-10 12:30:22 +01:00
}
protected override string GetBaseWhereClause ( )
{
2016-01-14 19:33:53 +01:00
return "umbracoNode.id = @id and nodeObjectType = @NodeObjectType" ;
2015-11-10 12:30:22 +01:00
}
protected override IEnumerable < string > GetDeleteClauses ( )
{
throw new NotImplementedException ( ) ;
}
protected override Guid NodeObjectTypeId
{
2016-01-14 19:33:53 +01:00
get { return _containerObjectType ; }
2015-11-10 12:30:22 +01:00
}
protected override void PersistDeletedItem ( EntityContainer entity )
{
2019-10-07 22:10:21 +02:00
if ( entity = = null ) throw new ArgumentNullException ( nameof ( entity ) ) ;
2016-01-26 13:43:56 +01:00
EnsureContainerType ( entity ) ;
2016-04-12 15:11:07 +02:00
var nodeDto = Database . FirstOrDefault < NodeDto > ( Sql ( ) . SelectAll ( )
. From < NodeDto > ( )
. Where < NodeDto > ( dto = > dto . NodeId = = entity . Id & & dto . NodeObjectType = = entity . ContainerObjectType ) ) ;
2015-11-20 13:39:42 +01:00
if ( nodeDto = = null ) return ;
// move children to the parent so they are not orphans
2016-04-12 15:11:07 +02:00
var childDtos = Database . Fetch < NodeDto > ( Sql ( ) . SelectAll ( )
. From < NodeDto > ( )
2015-11-20 13:39:42 +01:00
. Where ( "parentID=@parentID AND (nodeObjectType=@containedObjectType OR nodeObjectType=@containerObjectType)" ,
new
{
parentID = entity . Id ,
containedObjectType = entity . ContainedObjectType ,
containerObjectType = entity . ContainerObjectType
} ) ) ;
foreach ( var childDto in childDtos )
2015-11-10 12:30:22 +01:00
{
2015-11-20 13:39:42 +01:00
childDto . ParentId = nodeDto . ParentId ;
2015-11-10 12:30:22 +01:00
Database . Update ( childDto ) ;
}
2015-11-20 13:39:42 +01:00
// delete
Database . Delete ( nodeDto ) ;
2017-05-30 10:50:09 +02:00
2018-01-12 10:48:36 +01:00
entity . DeleteDate = DateTime . Now ;
2015-11-10 12:30:22 +01:00
}
protected override void PersistNewItem ( EntityContainer entity )
{
2019-10-07 22:10:21 +02:00
if ( entity = = null ) throw new ArgumentNullException ( nameof ( entity ) ) ;
2016-01-26 13:43:56 +01:00
EnsureContainerType ( entity ) ;
2019-11-14 00:57:13 +01:00
if ( entity . Name = = null ) throw new InvalidOperationException ( "Entity name can't be null." ) ;
if ( string . IsNullOrWhiteSpace ( entity . Name ) ) throw new InvalidOperationException ( "Entity name can't be empty or consist only of white-space characters." ) ;
2015-11-10 12:30:22 +01:00
entity . Name = entity . Name . Trim ( ) ;
2015-11-20 13:39:42 +01:00
// guard against duplicates
2016-04-12 15:11:07 +02:00
var nodeDto = Database . FirstOrDefault < NodeDto > ( Sql ( ) . SelectAll ( )
. From < NodeDto > ( )
. Where < NodeDto > ( dto = > dto . ParentId = = entity . ParentId & & dto . Text = = entity . Name & & dto . NodeObjectType = = entity . ContainerObjectType ) ) ;
2015-11-20 13:39:42 +01:00
if ( nodeDto ! = null )
throw new InvalidOperationException ( "A container with the same name already exists." ) ;
2015-11-10 12:30:22 +01:00
2015-11-20 13:39:42 +01:00
// create
2015-11-10 12:30:22 +01:00
var level = 0 ;
var path = "-1" ;
if ( entity . ParentId > - 1 )
{
2016-04-12 15:11:07 +02:00
var parentDto = Database . FirstOrDefault < NodeDto > ( Sql ( ) . SelectAll ( )
. From < NodeDto > ( )
. Where < NodeDto > ( dto = > dto . NodeId = = entity . ParentId & & dto . NodeObjectType = = entity . ContainerObjectType ) ) ;
2015-11-10 12:30:22 +01:00
2015-11-20 13:39:42 +01:00
if ( parentDto = = null )
2019-11-14 00:57:13 +01:00
throw new InvalidOperationException ( "Could not find parent container with id " + entity . ParentId ) ;
2015-11-20 13:39:42 +01:00
level = parentDto . Level ;
path = parentDto . Path ;
2015-11-10 12:30:22 +01:00
}
2015-11-20 13:39:42 +01:00
// note: sortOrder is NOT managed and always zero for containers
nodeDto = new NodeDto
2015-11-10 12:30:22 +01:00
{
CreateDate = DateTime . Now ,
Level = Convert . ToInt16 ( level + 1 ) ,
2015-11-20 13:39:42 +01:00
NodeObjectType = entity . ContainerObjectType ,
2015-11-10 12:30:22 +01:00
ParentId = entity . ParentId ,
Path = path ,
SortOrder = 0 ,
Text = entity . Name ,
2015-12-16 17:16:44 +01:00
UserId = entity . CreatorId ,
UniqueId = entity . Key
2015-11-10 12:30:22 +01:00
} ;
2015-11-20 13:39:42 +01:00
// insert, get the id, update the path with the id
var id = Convert . ToInt32 ( Database . Insert ( nodeDto ) ) ;
2015-11-10 12:30:22 +01:00
nodeDto . Path = nodeDto . Path + "," + nodeDto . NodeId ;
2016-04-12 15:11:07 +02:00
Database . Save < NodeDto > ( nodeDto ) ;
2015-11-10 12:30:22 +01:00
2015-11-20 13:39:42 +01:00
// refresh the entity
2015-11-10 16:14:03 +01:00
entity . Id = id ;
entity . Path = nodeDto . Path ;
2015-11-20 13:39:42 +01:00
entity . Level = nodeDto . Level ;
entity . SortOrder = 0 ;
entity . CreateDate = nodeDto . CreateDate ;
2015-11-10 12:30:22 +01:00
entity . ResetDirtyProperties ( ) ;
}
2015-11-20 13:39:42 +01:00
// beware! does NOT manage descendants in case of a new parent
//
2015-11-10 12:30:22 +01:00
protected override void PersistUpdatedItem ( EntityContainer entity )
{
2019-10-07 22:10:21 +02:00
if ( entity = = null ) throw new ArgumentNullException ( nameof ( entity ) ) ;
2016-01-26 13:43:56 +01:00
EnsureContainerType ( entity ) ;
2019-11-14 00:57:13 +01:00
if ( entity . Name = = null ) throw new InvalidOperationException ( "Entity name can't be null." ) ;
if ( string . IsNullOrWhiteSpace ( entity . Name ) ) throw new InvalidOperationException ( "Entity name can't be empty or consist only of white-space characters." ) ;
2015-11-20 13:39:42 +01:00
entity . Name = entity . Name . Trim ( ) ;
// find container to update
2016-04-12 15:11:07 +02:00
var nodeDto = Database . FirstOrDefault < NodeDto > ( Sql ( ) . SelectAll ( )
. From < NodeDto > ( )
. Where < NodeDto > ( dto = > dto . NodeId = = entity . Id & & dto . NodeObjectType = = entity . ContainerObjectType ) ) ;
2015-11-20 13:39:42 +01:00
if ( nodeDto = = null )
throw new InvalidOperationException ( "Could not find container with id " + entity . Id ) ;
// guard against duplicates
2016-04-12 15:11:07 +02:00
var dupNodeDto = Database . FirstOrDefault < NodeDto > ( Sql ( ) . SelectAll ( )
. From < NodeDto > ( )
. Where < NodeDto > ( dto = > dto . ParentId = = entity . ParentId & & dto . Text = = entity . Name & & dto . NodeObjectType = = entity . ContainerObjectType ) ) ;
2015-11-20 13:39:42 +01:00
if ( dupNodeDto ! = null & & dupNodeDto . NodeId ! = nodeDto . NodeId )
throw new InvalidOperationException ( "A container with the same name already exists." ) ;
// update
nodeDto . Text = entity . Name ;
if ( nodeDto . ParentId ! = entity . ParentId )
{
nodeDto . Level = 0 ;
nodeDto . Path = "-1" ;
if ( entity . ParentId > - 1 )
{
2016-04-12 15:11:07 +02:00
var parent = Database . FirstOrDefault < NodeDto > ( Sql ( ) . SelectAll ( )
. From < NodeDto > ( )
. Where < NodeDto > ( dto = > dto . NodeId = = entity . ParentId & & dto . NodeObjectType = = entity . ContainerObjectType ) ) ;
2015-11-20 13:39:42 +01:00
if ( parent = = null )
2019-11-14 00:57:13 +01:00
throw new InvalidOperationException ( "Could not find parent container with id " + entity . ParentId ) ;
2015-11-20 13:39:42 +01:00
nodeDto . Level = Convert . ToInt16 ( parent . Level + 1 ) ;
nodeDto . Path = parent . Path + "," + nodeDto . NodeId ;
}
nodeDto . ParentId = entity . ParentId ;
}
// note: sortOrder is NOT managed and always zero for containers
// update
Database . Update ( nodeDto ) ;
2017-07-20 11:21:28 +02:00
2015-11-20 13:39:42 +01:00
// refresh the entity
entity . Path = nodeDto . Path ;
entity . Level = nodeDto . Level ;
entity . SortOrder = 0 ;
entity . ResetDirtyProperties ( ) ;
2015-11-10 12:30:22 +01:00
}
2016-01-26 13:43:56 +01:00
private void EnsureContainerType ( EntityContainer entity )
{
if ( entity . ContainerObjectType ! = NodeObjectTypeId )
{
throw new InvalidOperationException ( "The container type does not match the repository object type" ) ;
}
}
2015-11-10 12:30:22 +01:00
}
2017-07-20 11:21:28 +02:00
}