2019-06-20 16:02:42 +02:00
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Globalization ;
using System.Net ;
using System.Text ;
using System.Web.Http ;
using AutoMapper ;
using Umbraco.Core ;
using Umbraco.Core.Models.Membership ;
using Umbraco.Web.Models.ContentEditing ;
using Umbraco.Web.Mvc ;
using System.Linq ;
using System.Net.Http ;
using Umbraco.Core.Models ;
using Constants = Umbraco . Core . Constants ;
using Examine ;
using Umbraco.Web.Dynamics ;
using System.Text.RegularExpressions ;
using Umbraco.Core.Persistence.DatabaseModelDefinitions ;
using System.Web.Http.Controllers ;
using Umbraco.Core.Models.EntityBase ;
2019-06-25 11:00:20 +02:00
using Umbraco.Core.Services ;
2019-06-20 16:02:42 +02:00
using Umbraco.Core.Xml ;
using Umbraco.Web.Search ;
using Umbraco.Web.Trees ;
namespace Umbraco.Web.Editors
{
/// <summary>
/// The API controller used for getting entity objects, basic name, icon, id representation of umbraco objects that are based on CMSNode
/// </summary>
/// <remarks>
2019-06-26 14:35:36 +10:00
/// <para>
/// This controller allows resolving basic entity data for various entities without placing the hard restrictions on users that may not have access
/// to the sections these entities entities exist in. This is to allow pickers, etc... of data to work for all users. In some cases such as accessing
/// Members, more explicit security checks are done.
/// </para>
/// <para>Some objects such as macros are not based on CMSNode</para>
2019-06-20 16:02:42 +02:00
/// </remarks>
[EntityControllerConfiguration]
[PluginController("UmbracoApi")]
public class EntityController : UmbracoAuthorizedJsonController
{
/// <summary>
/// Configures this controller with a custom action selector
/// </summary>
private class EntityControllerConfigurationAttribute : Attribute , IControllerConfiguration
{
public void Initialize ( HttpControllerSettings controllerSettings , HttpControllerDescriptor controllerDescriptor )
{
controllerSettings . Services . Replace ( typeof ( IHttpActionSelector ) , new ParameterSwapControllerActionSelector (
//This is a special case, we'll accept a String here so that we can get page members when the special "all-members"
//id is passed in eventually we'll probably want to support GUID + Udi too
new ParameterSwapControllerActionSelector . ParameterSwapInfo ( "GetPagedChildren" , "id" , typeof ( int ) , typeof ( string ) ) ,
new ParameterSwapControllerActionSelector . ParameterSwapInfo ( "GetPath" , "id" , typeof ( int ) , typeof ( Guid ) , typeof ( Udi ) ) ,
new ParameterSwapControllerActionSelector . ParameterSwapInfo ( "GetById" , "id" , typeof ( int ) , typeof ( Guid ) , typeof ( Udi ) ) ,
new ParameterSwapControllerActionSelector . ParameterSwapInfo ( "GetByIds" , "ids" , typeof ( int [ ] ) , typeof ( Guid [ ] ) , typeof ( Udi [ ] ) ) ) ) ;
}
}
private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher ( ) ;
/// <summary>
/// Returns an Umbraco alias given a string
/// </summary>
/// <param name="value"></param>
/// <param name="camelCase"></param>
/// <returns></returns>
public dynamic GetSafeAlias ( string value , bool camelCase = true )
{
var returnValue = ( string . IsNullOrWhiteSpace ( value ) ) ? string . Empty : value . ToSafeAlias ( camelCase ) ;
dynamic returnObj = new System . Dynamic . ExpandoObject ( ) ;
returnObj . alias = returnValue ;
returnObj . original = value ;
returnObj . camelCase = camelCase ;
return returnObj ;
}
/// <summary>
/// Searches for results based on the entity type
/// </summary>
/// <param name="query"></param>
/// <param name="type"></param>
/// <param name="searchFrom">
/// A starting point for the search, generally a node id, but for members this is a member type alias
/// </param>
/// <param name="dataTypeId">If set used to look up whether user and group start node permissions will be ignored.</param>
/// <returns></returns>
[HttpGet]
2019-06-25 11:00:20 +02:00
public IEnumerable < EntityBasic > Search ( string query , UmbracoEntityTypes type , string searchFrom = null , Guid ? dataTypeId = null )
2019-06-20 16:02:42 +02:00
{
2019-06-26 14:35:36 +10:00
// NOTE: Theoretically you shouldn't be able to see member data if you don't have access to members right? ... but there is a member picker, so can't really do that
2019-06-20 16:02:42 +02:00
if ( string . IsNullOrEmpty ( query ) )
return Enumerable . Empty < EntityBasic > ( ) ;
var ignoreUserStartNodes = dataTypeId . HasValue & & Services . DataTypeService . IsDataTypeIgnoringUserStartNodes ( dataTypeId . Value ) ;
return ExamineSearch ( query , type , searchFrom , ignoreUserStartNodes ) ;
}
/// <summary>
/// Searches for all content that the user is allowed to see (based on their allowed sections)
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
/// <remarks>
/// Even though a normal entity search will allow any user to search on a entity type that they may not have access to edit, we need
/// to filter these results to the sections they are allowed to edit since this search function is explicitly for the global search
/// so if we showed entities that they weren't allowed to edit they would get errors when clicking on the result.
///
/// The reason a user is allowed to search individual entity types that they are not allowed to edit is because those search
/// methods might be used in things like pickers in the content editor.
/// </remarks>
[HttpGet]
public IDictionary < string , TreeSearchResult > SearchAll ( string query )
{
var result = new Dictionary < string , TreeSearchResult > ( ) ;
if ( string . IsNullOrEmpty ( query ) )
return result ;
var allowedSections = Security . CurrentUser . AllowedSections . ToArray ( ) ;
var searchableTrees = SearchableTreeResolver . Current . GetSearchableTrees ( ) ;
foreach ( var searchableTree in searchableTrees )
{
if ( allowedSections . Contains ( searchableTree . Value . AppAlias ) )
{
var tree = Services . ApplicationTreeService . GetByAlias ( searchableTree . Key ) ;
if ( tree = = null ) continue ; //shouldn't occur
var searchableTreeAttribute = searchableTree . Value . SearchableTree . GetType ( ) . GetCustomAttribute < SearchableTreeAttribute > ( false ) ;
var treeAttribute = tree . GetTreeAttribute ( ) ;
long total ;
result [ treeAttribute . GetRootNodeDisplayName ( Services . TextService ) ] = new TreeSearchResult
{
Results = searchableTree . Value . SearchableTree . Search ( query , 200 , 0 , out total ) ,
TreeAlias = searchableTree . Key ,
AppAlias = searchableTree . Value . AppAlias ,
JsFormatterService = searchableTreeAttribute = = null ? "" : searchableTreeAttribute . ServiceName ,
JsFormatterMethod = searchableTreeAttribute = = null ? "" : searchableTreeAttribute . MethodName
} ;
}
}
return result ;
}
/// <summary>
/// Gets the path for a given node ID
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
public IEnumerable < int > GetPath ( int id , UmbracoEntityTypes type )
{
var foundContent = GetResultForId ( id , type ) ;
return foundContent . Path . Split ( new [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries ) . Select ( int . Parse ) ;
}
/// <summary>
/// Gets the path for a given node ID
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
public IEnumerable < int > GetPath ( Guid id , UmbracoEntityTypes type )
{
var foundContent = GetResultForKey ( id , type ) ;
return foundContent . Path . Split ( new [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries ) . Select ( int . Parse ) ;
}
/// <summary>
/// Gets the path for a given node ID
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
public IEnumerable < int > GetPath ( Udi id , UmbracoEntityTypes type )
{
var guidUdi = id as GuidUdi ;
if ( guidUdi ! = null )
{
return GetPath ( guidUdi . Guid , type ) ;
}
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
/// <summary>
/// Gets the url of an entity
/// </summary>
/// <param name="id">Int id of the entity to fetch URL for</param>
/// <param name="type">The tpye of entity such as Document, Media, Member</param>
/// <returns>The URL or path to the item</returns>
2019-06-26 14:35:36 +10:00
/// <remarks>
/// We are not restricting this with security because there is no sensitive data
/// </remarks>
2019-06-20 16:02:42 +02:00
public HttpResponseMessage GetUrl ( int id , UmbracoEntityTypes type )
{
var returnUrl = string . Empty ;
if ( type = = UmbracoEntityTypes . Document )
{
var foundUrl = Umbraco . Url ( id ) ;
if ( string . IsNullOrEmpty ( foundUrl ) = = false & & foundUrl ! = "#" )
{
returnUrl = foundUrl ;
return new HttpResponseMessage ( HttpStatusCode . OK )
{
Content = new StringContent ( returnUrl )
} ;
}
}
var ancestors = GetAncestors ( id , type ) ;
//if content, skip the first node for replicating NiceUrl defaults
if ( type = = UmbracoEntityTypes . Document ) {
ancestors = ancestors . Skip ( 1 ) ;
}
returnUrl = "/" + string . Join ( "/" , ancestors . Select ( x = > x . Name ) ) ;
return new HttpResponseMessage ( HttpStatusCode . OK )
{
Content = new StringContent ( returnUrl )
} ;
}
[Obsolete("Use GetyById instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public EntityBasic GetByKey ( Guid id , UmbracoEntityTypes type )
{
return GetResultForKey ( id , type ) ;
}
/// <summary>
/// Gets an entity by a xpath query
/// </summary>
/// <param name="query"></param>
/// <param name="nodeContextId"></param>
/// <param name="type"></param>
/// <returns></returns>
public EntityBasic GetByQuery ( string query , int nodeContextId , UmbracoEntityTypes type )
{
//TODO: Rename this!!! It's misleading, it should be GetByXPath
if ( type ! = UmbracoEntityTypes . Document )
throw new ArgumentException ( "Get by query is only compatible with enitities of type Document" ) ;
var q = ParseXPathQuery ( query , nodeContextId ) ;
var node = Umbraco . TypedContentSingleAtXPath ( q ) ;
if ( node = = null )
return null ;
return GetById ( node . Id , type ) ;
}
//PP: wip in progress on the query parser
private string ParseXPathQuery ( string query , int id )
{
return UmbracoXPathPathSyntaxParser . ParseXPathQuery (
xpathExpression : query ,
nodeContextId : id ,
getPath : nodeid = >
{
var ent = Services . EntityService . Get ( nodeid ) ;
return ent . Path . Split ( ',' ) . Reverse ( ) ;
} ,
publishedContentExists : i = > Umbraco . TypedContent ( i ) ! = null ) ;
}
2019-06-24 10:52:35 +02:00
[HttpGet]
2019-06-25 13:18:15 +02:00
public UrlAndAnchors GetUrlAndAnchors ( [ FromUri ] int id )
2019-06-24 10:52:35 +02:00
{
var url = Umbraco . Url ( id ) ;
var anchorValues = Services . ContentService . GetAnchorValuesFromRTEs ( id ) ;
return new UrlAndAnchors ( url , anchorValues ) ;
}
2019-06-25 13:18:15 +02:00
public class AnchorsModel
2019-06-24 10:52:35 +02:00
{
2019-06-25 13:18:15 +02:00
public string RteContent { get ; set ; }
}
2019-06-24 10:52:35 +02:00
2019-06-25 13:18:15 +02:00
[HttpGet]
[HttpPost]
public IEnumerable < string > GetAnchors ( AnchorsModel model )
{
var anchorValues = Services . ContentService . GetAnchorValuesFromRTEContent ( model . RteContent ) ;
2019-06-24 10:52:35 +02:00
return anchorValues ;
}
2019-06-20 16:02:42 +02:00
#region GetById
/// <summary>
/// Gets an entity by it's id
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
public EntityBasic GetById ( int id , UmbracoEntityTypes type )
{
return GetResultForId ( id , type ) ;
}
/// <summary>
/// Gets an entity by it's key
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
public EntityBasic GetById ( Guid id , UmbracoEntityTypes type )
{
return GetResultForKey ( id , type ) ;
}
/// <summary>
/// Gets an entity by it's UDI
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
public EntityBasic GetById ( Udi id , UmbracoEntityTypes type )
{
var guidUdi = id as GuidUdi ;
if ( guidUdi ! = null )
{
return GetResultForKey ( guidUdi . Guid , type ) ;
}
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
#endregion
#region GetByIds
/// <summary>
/// Get entities by integer ids
/// </summary>
/// <param name="ids"></param>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>
/// We allow for POST because there could be quite a lot of Ids
/// </remarks>
[HttpGet]
[HttpPost]
public IEnumerable < EntityBasic > GetByIds ( [ FromJsonPath ] int [ ] ids , UmbracoEntityTypes type )
{
if ( ids = = null )
{
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
return GetResultForIds ( ids , type ) ;
}
/// <summary>
/// Get entities by GUID ids
/// </summary>
/// <param name="ids"></param>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>
/// We allow for POST because there could be quite a lot of Ids
/// </remarks>
[HttpGet]
[HttpPost]
public IEnumerable < EntityBasic > GetByIds ( [ FromJsonPath ] Guid [ ] ids , UmbracoEntityTypes type )
{
if ( ids = = null )
{
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
return GetResultForKeys ( ids , type ) ;
}
/// <summary>
/// Get entities by UDIs
/// </summary>
/// <param name="ids">
/// A list of UDIs to lookup items by, all UDIs must be of the same UDI type!
/// </param>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>
/// We allow for POST because there could be quite a lot of Ids.
/// </remarks>
[HttpGet]
[HttpPost]
public IEnumerable < EntityBasic > GetByIds ( [ FromJsonPath ] Udi [ ] ids , [ FromUri ] UmbracoEntityTypes type )
{
if ( ids = = null )
{
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
if ( ids . Length = = 0 )
{
return Enumerable . Empty < EntityBasic > ( ) ;
}
//all udi types will need to be the same in this list so we'll determine by the first
//currently we only support GuidIdi for this method
var guidUdi = ids [ 0 ] as GuidUdi ;
if ( guidUdi ! = null )
{
return GetResultForKeys ( ids . Select ( x = > ( ( GuidUdi ) x ) . Guid ) . ToArray ( ) , type ) ;
}
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
#endregion
[Obsolete("Use GetyByIds instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable < EntityBasic > GetByKeys ( [ FromUri ] Guid [ ] ids , UmbracoEntityTypes type )
{
if ( ids = = null )
{
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
return GetResultForKeys ( ids , type ) ;
}
2019-06-26 14:35:36 +10:00
public IEnumerable < EntityBasic > GetChildren ( int id , UmbracoEntityTypes type , Guid ? dataTypeId = null )
2019-06-20 16:02:42 +02:00
{
2019-06-26 14:35:36 +10:00
var objectType = ConvertToObjectType ( type ) ;
if ( objectType . HasValue )
{
//TODO: Need to check for Object types that support hierarchy here, some might not.
int [ ] startNodes = null ;
switch ( type )
{
case UmbracoEntityTypes . Document :
startNodes = Security . CurrentUser . CalculateContentStartNodeIds ( Services . EntityService ) ;
break ;
case UmbracoEntityTypes . Media :
startNodes = Security . CurrentUser . CalculateMediaStartNodeIds ( Services . EntityService ) ;
break ;
}
var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes ( dataTypeId ) ;
// root is special: we reduce it to start nodes if the user's start node is not the default, then we need to return their start nodes
if ( id = = Constants . System . Root & & startNodes . Length > 0 & & startNodes . Contains ( Constants . System . Root ) = = false & & ! ignoreUserStartNodes )
{
var nodes = Services . EntityService . GetAll ( objectType . Value , startNodes ) . ToArray ( ) ;
if ( nodes . Length = = 0 )
return Enumerable . Empty < EntityBasic > ( ) ;
var pr = new List < EntityBasic > ( nodes . Select ( Mapper . Map < EntityBasic > ) ) ;
return pr ;
}
// else proceed as usual
return Services . EntityService . GetChildren ( id , objectType . Value )
. WhereNotNull ( )
. Select ( Mapper . Map < EntityBasic > ) ;
}
//now we need to convert the unknown ones
switch ( type )
{
case UmbracoEntityTypes . Domain :
case UmbracoEntityTypes . Language :
case UmbracoEntityTypes . User :
case UmbracoEntityTypes . Macro :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + type ) ;
}
2019-06-20 16:02:42 +02:00
}
/// <summary>
/// Get paged child entities by id
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <returns></returns>
public PagedResult < EntityBasic > GetPagedChildren (
string id ,
UmbracoEntityTypes type ,
int pageNumber ,
int pageSize ,
string orderBy = "SortOrder" ,
Direction orderDirection = Direction . Ascending ,
2019-06-26 14:35:36 +10:00
string filter = "" ,
Guid ? dataTypeId = null )
2019-06-20 16:02:42 +02:00
{
int intId ;
if ( int . TryParse ( id , out intId ) )
{
return GetPagedChildren ( intId , type , pageNumber , pageSize , orderBy , orderDirection , filter ) ;
}
Guid guidId ;
if ( Guid . TryParse ( id , out guidId ) )
{
//Not supported currently
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
Udi udiId ;
if ( Udi . TryParse ( id , out udiId ) )
{
//Not supported currently
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
//so we don't have an INT, GUID or UDI, it's just a string, so now need to check if it's a special id or a member type
if ( id = = Constants . Conventions . MemberTypes . AllMembersListId )
{
//the EntityService can search paged members from the root
intId = - 1 ;
2019-06-26 14:35:36 +10:00
return GetPagedChildren ( intId , type , pageNumber , pageSize , orderBy , orderDirection , filter , dataTypeId ) ;
2019-06-20 16:02:42 +02:00
}
//the EntityService cannot search members of a certain type, this is currently not supported and would require
//quite a bit of plumbing to do in the Services/Repository, we'll revert to a paged search
long total ;
var searchResult = _treeSearcher . ExamineSearch ( Umbraco , filter ? ? "" , type , pageSize , pageNumber - 1 , out total , id ) ;
return new PagedResult < EntityBasic > ( total , pageNumber , pageSize )
{
Items = searchResult
} ;
}
/// <summary>
/// Get paged child entities by id
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <returns></returns>
public PagedResult < EntityBasic > GetPagedChildren (
int id ,
UmbracoEntityTypes type ,
int pageNumber ,
int pageSize ,
string orderBy = "SortOrder" ,
Direction orderDirection = Direction . Ascending ,
2019-06-26 14:35:36 +10:00
string filter = "" ,
Guid ? dataTypeId = null )
2019-06-20 16:02:42 +02:00
{
if ( pageNumber < = 0 )
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
if ( pageSize < = 0 )
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
var objectType = ConvertToObjectType ( type ) ;
if ( objectType . HasValue )
{
2019-06-26 14:35:36 +10:00
IEnumerable < IUmbracoEntity > entities ;
2019-06-20 16:02:42 +02:00
long totalRecords ;
2019-06-26 14:35:36 +10:00
int [ ] startNodes = null ;
switch ( type )
{
case UmbracoEntityTypes . Document :
startNodes = Security . CurrentUser . CalculateContentStartNodeIds ( Services . EntityService ) ;
break ;
case UmbracoEntityTypes . Media :
startNodes = Security . CurrentUser . CalculateMediaStartNodeIds ( Services . EntityService ) ;
break ;
}
var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes ( dataTypeId ) ;
// root is special: we reduce it to start nodes if the user's start node is not the default, then we need to return their start nodes
if ( id = = Constants . System . Root & & startNodes . Length > 0 & & startNodes . Contains ( Constants . System . Root ) = = false & & ! ignoreUserStartNodes )
{
if ( pageNumber > 0 )
return new PagedResult < EntityBasic > ( 0 , 0 , 0 ) ;
var nodes = Services . EntityService . GetAll ( objectType . Value , startNodes ) . ToArray ( ) ;
if ( nodes . Length = = 0 )
return new PagedResult < EntityBasic > ( 0 , 0 , 0 ) ;
if ( pageSize < nodes . Length ) pageSize = nodes . Length ; // bah
var pr = new PagedResult < EntityBasic > ( nodes . Length , pageNumber , pageSize )
{
Items = nodes . Select ( Mapper . Map < EntityBasic > )
} ;
return pr ;
}
// else proceed as usual
entities = Services . EntityService . GetPagedChildren ( id , objectType . Value , pageNumber - 1 , pageSize , out totalRecords , orderBy , orderDirection , filter ) ;
2019-06-20 16:02:42 +02:00
if ( totalRecords = = 0 )
{
return new PagedResult < EntityBasic > ( 0 , 0 , 0 ) ;
}
var pagedResult = new PagedResult < EntityBasic > ( totalRecords , pageNumber , pageSize )
{
Items = entities . Select ( Mapper . Map < EntityBasic > )
} ;
return pagedResult ;
}
//now we need to convert the unknown ones
switch ( type )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
case UmbracoEntityTypes . Domain :
case UmbracoEntityTypes . Language :
case UmbracoEntityTypes . User :
case UmbracoEntityTypes . Macro :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + type ) ;
}
}
public PagedResult < EntityBasic > GetPagedDescendants (
int id ,
UmbracoEntityTypes type ,
int pageNumber ,
int pageSize ,
string orderBy = "SortOrder" ,
Direction orderDirection = Direction . Ascending ,
2019-06-25 11:00:20 +02:00
string filter = "" ,
Guid ? dataTypeId = null )
2019-06-20 16:02:42 +02:00
{
if ( pageNumber < = 0 )
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
if ( pageSize < = 0 )
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
var objectType = ConvertToObjectType ( type ) ;
if ( objectType . HasValue )
{
IEnumerable < IUmbracoEntity > entities ;
long totalRecords ;
if ( id = = Constants . System . Root )
{
// root is special: we reduce it to start nodes
int [ ] aids = null ;
switch ( type )
{
case UmbracoEntityTypes . Document :
aids = Security . CurrentUser . CalculateContentStartNodeIds ( Services . EntityService ) ;
break ;
case UmbracoEntityTypes . Media :
aids = Security . CurrentUser . CalculateMediaStartNodeIds ( Services . EntityService ) ;
break ;
}
var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes ( dataTypeId ) ;
entities = aids = = null | | aids . Contains ( Constants . System . Root ) | | ignoreUserStartNodes
? Services . EntityService . GetPagedDescendantsFromRoot ( objectType . Value , pageNumber - 1 , pageSize , out totalRecords , orderBy , orderDirection , filter , includeTrashed : false )
: Services . EntityService . GetPagedDescendants ( aids , objectType . Value , pageNumber - 1 , pageSize , out totalRecords , orderBy , orderDirection , filter ) ;
}
else
{
entities = Services . EntityService . GetPagedDescendants ( id , objectType . Value , pageNumber - 1 , pageSize , out totalRecords , orderBy , orderDirection , filter ) ;
}
if ( totalRecords = = 0 )
{
return new PagedResult < EntityBasic > ( 0 , 0 , 0 ) ;
}
var pagedResult = new PagedResult < EntityBasic > ( totalRecords , pageNumber , pageSize )
{
Items = entities . Select ( Mapper . Map < EntityBasic > )
} ;
return pagedResult ;
}
//now we need to convert the unknown ones
switch ( type )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
case UmbracoEntityTypes . Domain :
case UmbracoEntityTypes . Language :
case UmbracoEntityTypes . User :
case UmbracoEntityTypes . Macro :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + type ) ;
}
}
private bool IsDataTypeIgnoringUserStartNodes ( Guid ? dataTypeId ) = > dataTypeId . HasValue & & Services . DataTypeService . IsDataTypeIgnoringUserStartNodes ( dataTypeId . Value ) ;
2019-06-25 11:00:20 +02:00
public IEnumerable < EntityBasic > GetAncestors ( int id , UmbracoEntityTypes type , Guid ? dataTypeId = null )
2019-06-20 16:02:42 +02:00
{
return GetResultForAncestors ( id , type , dataTypeId ) ;
}
public IEnumerable < EntityBasic > GetAll ( UmbracoEntityTypes type , string postFilter , [ FromUri ] IDictionary < string , object > postFilterParams )
{
return GetResultForAll ( type , postFilter , postFilterParams ) ;
}
/// <summary>
/// Searches for results based on the entity type
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="searchFrom"></param>
/// <param name="ignoreUserStartNodes">If set to true, user and group start node permissions will be ignored.</param>
/// <returns></returns>
private IEnumerable < SearchResultItem > ExamineSearch ( string query , UmbracoEntityTypes entityType , string searchFrom = null , bool ignoreUserStartNodes = false )
{
long total ;
2019-06-25 11:00:20 +02:00
return _treeSearcher . ExamineSearch ( Umbraco , query , entityType , 200 , 0 , out total , searchFrom , ignoreUserStartNodes ) ;
2019-06-20 16:02:42 +02:00
}
private IEnumerable < EntityBasic > GetResultForAncestors ( int id , UmbracoEntityTypes entityType , Guid ? dataTypeId = null )
{
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
//TODO: Need to check for Object types that support hierarchic here, some might not.
var ids = Services . EntityService . Get ( id ) . Path . Split ( ',' ) . Select ( int . Parse ) . Distinct ( ) . ToArray ( ) ;
var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes ( dataTypeId ) ;
if ( ignoreUserStartNodes = = false )
{
int [ ] aids = null ;
switch ( entityType )
{
case UmbracoEntityTypes . Document :
aids = Security . CurrentUser . CalculateContentStartNodeIds ( Services . EntityService ) ;
break ;
case UmbracoEntityTypes . Media :
aids = Security . CurrentUser . CalculateMediaStartNodeIds ( Services . EntityService ) ;
break ;
}
if ( aids ! = null )
{
var lids = new List < int > ( ) ;
var ok = false ;
foreach ( var i in ids )
{
if ( ok )
{
lids . Add ( i ) ;
continue ;
}
if ( aids . Contains ( i ) )
{
lids . Add ( i ) ;
ok = true ;
}
}
ids = lids . ToArray ( ) ;
}
}
return ids . Length = = 0
? Enumerable . Empty < EntityBasic > ( )
: Services . EntityService . GetAll ( objectType . Value , ids )
. WhereNotNull ( )
. OrderBy ( x = > x . Level )
. Select ( Mapper . Map < EntityBasic > ) ;
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
case UmbracoEntityTypes . Domain :
case UmbracoEntityTypes . Language :
case UmbracoEntityTypes . User :
case UmbracoEntityTypes . Macro :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + entityType ) ;
}
}
/// <summary>
/// Gets the result for the entity list based on the type
/// </summary>
/// <param name="entityType"></param>
/// <param name="postFilter">A string where filter that will filter the results dynamically with linq - optional</param>
/// <param name="postFilterParams">the parameters to fill in the string where filter - optional</param>
/// <returns></returns>
private IEnumerable < EntityBasic > GetResultForAll ( UmbracoEntityTypes entityType , string postFilter = null , IDictionary < string , object > postFilterParams = null )
{
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
//TODO: Should we order this by something ?
var entities = Services . EntityService . GetAll ( objectType . Value ) . WhereNotNull ( ) . Select ( Mapper . Map < EntityBasic > ) ;
return ExecutePostFilter ( entities , postFilter , postFilterParams ) ;
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . Template :
var templates = Services . FileService . GetTemplates ( ) ;
var filteredTemplates = ExecutePostFilter ( templates , postFilter , postFilterParams ) ;
return filteredTemplates . Select ( Mapper . Map < EntityBasic > ) ;
case UmbracoEntityTypes . Macro :
//Get all macros from the macro service
var macros = Services . MacroService . GetAll ( ) . WhereNotNull ( ) . OrderBy ( x = > x . Name ) ;
var filteredMacros = ExecutePostFilter ( macros , postFilter , postFilterParams ) ;
return filteredMacros . Select ( Mapper . Map < EntityBasic > ) ;
case UmbracoEntityTypes . PropertyType :
//get all document types, then combine all property types into one list
var propertyTypes = Services . ContentTypeService . GetAllContentTypes ( ) . Cast < IContentTypeComposition > ( )
. Concat ( Services . ContentTypeService . GetAllMediaTypes ( ) )
. ToArray ( )
. SelectMany ( x = > x . PropertyTypes )
. DistinctBy ( composition = > composition . Alias ) ;
var filteredPropertyTypes = ExecutePostFilter ( propertyTypes , postFilter , postFilterParams ) ;
return Mapper . Map < IEnumerable < PropertyType > , IEnumerable < EntityBasic > > ( filteredPropertyTypes ) ;
case UmbracoEntityTypes . PropertyGroup :
//get all document types, then combine all property types into one list
var propertyGroups = Services . ContentTypeService . GetAllContentTypes ( ) . Cast < IContentTypeComposition > ( )
. Concat ( Services . ContentTypeService . GetAllMediaTypes ( ) )
. ToArray ( )
. SelectMany ( x = > x . PropertyGroups )
. DistinctBy ( composition = > composition . Name ) ;
var filteredpropertyGroups = ExecutePostFilter ( propertyGroups , postFilter , postFilterParams ) ;
return Mapper . Map < IEnumerable < PropertyGroup > , IEnumerable < EntityBasic > > ( filteredpropertyGroups ) ;
case UmbracoEntityTypes . User :
int total ;
var users = Services . UserService . GetAll ( 0 , int . MaxValue , out total ) ;
var filteredUsers = ExecutePostFilter ( users , postFilter , postFilterParams ) ;
return Mapper . Map < IEnumerable < IUser > , IEnumerable < EntityBasic > > ( filteredUsers ) ;
case UmbracoEntityTypes . Domain :
case UmbracoEntityTypes . Language :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + entityType ) ;
}
}
private IEnumerable < EntityBasic > GetResultForKeys ( Guid [ ] keys , UmbracoEntityTypes entityType )
{
if ( keys . Length = = 0 )
return Enumerable . Empty < EntityBasic > ( ) ;
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
var entities = Services . EntityService . GetAll ( objectType . Value , keys )
. WhereNotNull ( )
. Select ( Mapper . Map < EntityBasic > ) ;
// entities are in "some" order, put them back in order
var xref = entities . ToDictionary ( x = > x . Key ) ;
var result = keys . Select ( x = > xref . ContainsKey ( x ) ? xref [ x ] : null ) . Where ( x = > x ! = null ) ;
return result ;
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
case UmbracoEntityTypes . Domain :
case UmbracoEntityTypes . Language :
case UmbracoEntityTypes . User :
case UmbracoEntityTypes . Macro :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + entityType ) ;
}
}
private IEnumerable < EntityBasic > GetResultForIds ( int [ ] ids , UmbracoEntityTypes entityType )
{
if ( ids . Length = = 0 )
return Enumerable . Empty < EntityBasic > ( ) ;
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
var entities = Services . EntityService . GetAll ( objectType . Value , ids )
. WhereNotNull ( )
. Select ( Mapper . Map < EntityBasic > ) ;
// entities are in "some" order, put them back in order
var xref = entities . ToDictionary ( x = > x . Id ) ;
var result = ids . Select ( x = > xref . ContainsKey ( x ) ? xref [ x ] : null ) . Where ( x = > x ! = null ) ;
return result ;
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
case UmbracoEntityTypes . Domain :
case UmbracoEntityTypes . Language :
case UmbracoEntityTypes . User :
case UmbracoEntityTypes . Macro :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + entityType ) ;
}
}
private EntityBasic GetResultForKey ( Guid key , UmbracoEntityTypes entityType )
{
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
var found = Services . EntityService . GetByKey ( key , objectType . Value ) ;
if ( found = = null )
{
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
return Mapper . Map < EntityBasic > ( found ) ;
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
case UmbracoEntityTypes . Domain :
case UmbracoEntityTypes . Language :
case UmbracoEntityTypes . User :
case UmbracoEntityTypes . Macro :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + entityType ) ;
}
}
private EntityBasic GetResultForId ( int id , UmbracoEntityTypes entityType )
{
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
var found = Services . EntityService . Get ( id , objectType . Value ) ;
if ( found = = null )
{
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
return Mapper . Map < EntityBasic > ( found ) ;
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
case UmbracoEntityTypes . Domain :
case UmbracoEntityTypes . Language :
case UmbracoEntityTypes . User :
case UmbracoEntityTypes . Macro :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + entityType ) ;
}
}
private static UmbracoObjectTypes ? ConvertToObjectType ( UmbracoEntityTypes entityType )
{
switch ( entityType )
{
case UmbracoEntityTypes . Document :
return UmbracoObjectTypes . Document ;
case UmbracoEntityTypes . Media :
return UmbracoObjectTypes . Media ;
case UmbracoEntityTypes . MemberType :
return UmbracoObjectTypes . MediaType ;
case UmbracoEntityTypes . MemberGroup :
return UmbracoObjectTypes . MemberGroup ;
case UmbracoEntityTypes . ContentItem :
return UmbracoObjectTypes . ContentItem ;
case UmbracoEntityTypes . MediaType :
return UmbracoObjectTypes . MediaType ;
case UmbracoEntityTypes . DocumentType :
return UmbracoObjectTypes . DocumentType ;
case UmbracoEntityTypes . Stylesheet :
return UmbracoObjectTypes . Stylesheet ;
case UmbracoEntityTypes . Member :
return UmbracoObjectTypes . Member ;
case UmbracoEntityTypes . DataType :
return UmbracoObjectTypes . DataType ;
default :
//There is no UmbracoEntity conversion (things like Macros, Users, etc...)
return null ;
}
}
/// <summary>
/// Executes the post filter against a collection of objects
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entities"></param>
/// <param name="postFilter"></param>
/// <param name="postFilterParams"></param>
/// <returns></returns>
private IEnumerable < T > ExecutePostFilter < T > ( IEnumerable < T > entities , string postFilter , IDictionary < string , object > postFilterParams )
{
//if a post filter is assigned then try to execute it
if ( postFilter . IsNullOrWhiteSpace ( ) = = false )
{
return postFilterParams = = null
? entities . AsQueryable ( ) . Where ( postFilter ) . ToArray ( )
: entities . AsQueryable ( ) . Where ( postFilter , postFilterParams ) . ToArray ( ) ;
}
return entities ;
}
}
}