2021-01-12 14:00:14 +01:00
using System ;
2018-06-29 19:52:40 +02:00
using System.Collections.Generic ;
2021-09-14 22:13:39 +02:00
using System.Globalization ;
2018-06-29 19:52:40 +02:00
using System.Linq ;
2019-01-23 10:22:29 +01:00
using System.Reflection ;
2020-06-15 13:07:49 +02:00
using Microsoft.AspNetCore.Http ;
using Microsoft.AspNetCore.Mvc ;
2021-01-14 19:41:32 +01:00
using Microsoft.AspNetCore.Mvc.Infrastructure ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core ;
2021-03-05 15:36:27 +01:00
using Umbraco.Cms.Core.Cache ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core.Mapping ;
using Umbraco.Cms.Core.Models ;
using Umbraco.Cms.Core.Models.ContentEditing ;
using Umbraco.Cms.Core.Models.Entities ;
using Umbraco.Cms.Core.Models.Membership ;
using Umbraco.Cms.Core.Models.TemplateQuery ;
using Umbraco.Cms.Core.Routing ;
using Umbraco.Cms.Core.Security ;
using Umbraco.Cms.Core.Services ;
using Umbraco.Cms.Core.Strings ;
using Umbraco.Cms.Core.Trees ;
using Umbraco.Cms.Core.Xml ;
2021-02-12 13:36:50 +01:00
using Umbraco.Cms.Infrastructure.Persistence ;
2021-02-15 11:45:27 +01:00
using Umbraco.Cms.Infrastructure.Search ;
2021-02-10 11:11:18 +01:00
using Umbraco.Cms.Web.BackOffice.ModelBinders ;
2021-02-10 11:42:04 +01:00
using Umbraco.Cms.Web.Common.Attributes ;
using Umbraco.Cms.Web.Common.ModelBinders ;
2020-06-15 13:07:49 +02:00
using Umbraco.Extensions ;
2021-02-09 10:22:42 +01:00
using Constants = Umbraco . Cms . Core . Constants ;
2019-11-05 13:45:42 +01:00
2021-02-10 11:11:18 +01:00
namespace Umbraco.Cms.Web.BackOffice.Controllers
2018-06-29 19:52:40 +02:00
{
/// <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-28 13:03: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>
2018-06-29 19:52:40 +02:00
/// </remarks>
2020-08-04 12:27:21 +10:00
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
2021-09-08 14:59:52 +02:00
[ParameterSwapControllerActionSelector(nameof(GetAncestors), "id", typeof(int), typeof(Guid))]
2021-01-29 10:30:28 +01:00
[ParameterSwapControllerActionSelector(nameof(GetPagedChildren), "id", typeof(int), typeof(string))]
[ParameterSwapControllerActionSelector(nameof(GetPath), "id", typeof(int), typeof(Guid), typeof(Udi))]
[ParameterSwapControllerActionSelector(nameof(GetUrlAndAnchors), "id", typeof(int), typeof(Guid), typeof(Udi))]
[ParameterSwapControllerActionSelector(nameof(GetById), "id", typeof(int), typeof(Guid), typeof(Udi))]
[ParameterSwapControllerActionSelector(nameof(GetByIds), "ids", typeof(int[] ) , typeof ( Guid [ ] ) , typeof ( Udi [ ] ) ) ]
[ParameterSwapControllerActionSelector(nameof(GetUrl), "id", typeof(int), typeof(Udi))]
2018-06-29 19:52:40 +02:00
public class EntityController : UmbracoAuthorizedJsonController
{
2019-01-17 16:40:11 +11:00
private readonly ITreeService _treeService ;
2019-01-24 11:28:27 +01:00
private readonly UmbracoTreeSearcher _treeSearcher ;
private readonly SearchableTreeCollection _searchableTreeCollection ;
2020-03-03 11:59:17 +01:00
private readonly IPublishedContentQuery _publishedContentQuery ;
2020-06-15 13:07:49 +02:00
private readonly IShortStringHelper _shortStringHelper ;
private readonly IEntityService _entityService ;
2020-10-21 16:51:00 +11:00
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor ;
2020-06-15 13:07:49 +02:00
private readonly IPublishedUrlProvider _publishedUrlProvider ;
private readonly IContentService _contentService ;
2021-04-20 19:34:18 +02:00
private readonly IUmbracoMapper _umbracoMapper ;
2020-06-15 13:07:49 +02:00
private readonly IDataTypeService _dataTypeService ;
private readonly ISqlContext _sqlContext ;
private readonly ILocalizedTextService _localizedTextService ;
private readonly IFileService _fileService ;
private readonly IContentTypeService _contentTypeService ;
private readonly IMediaTypeService _mediaTypeService ;
private readonly IMacroService _macroService ;
private readonly IUserService _userService ;
private readonly ILocalizationService _localizationService ;
2021-03-05 15:36:27 +01:00
private readonly AppCaches _appCaches ;
2019-01-17 13:20:19 +11:00
2019-12-18 18:55:00 +01:00
public EntityController (
ITreeService treeService ,
UmbracoTreeSearcher treeSearcher ,
2020-06-15 13:07:49 +02:00
SearchableTreeCollection searchableTreeCollection ,
IPublishedContentQuery publishedContentQuery ,
2020-01-20 14:15:54 -08:00
IShortStringHelper shortStringHelper ,
2020-06-15 13:07:49 +02:00
IEntityService entityService ,
2020-10-21 16:51:00 +11:00
IBackOfficeSecurityAccessor backofficeSecurityAccessor ,
2020-03-03 11:59:17 +01:00
IPublishedUrlProvider publishedUrlProvider ,
2020-06-15 13:07:49 +02:00
IContentService contentService ,
2021-04-20 19:34:18 +02:00
IUmbracoMapper umbracoMapper ,
2020-06-15 13:07:49 +02:00
IDataTypeService dataTypeService ,
ISqlContext sqlContext ,
ILocalizedTextService localizedTextService ,
IFileService fileService ,
IContentTypeService contentTypeService ,
IMediaTypeService mediaTypeService ,
IMacroService macroService ,
IUserService userService ,
2021-03-05 15:36:27 +01:00
ILocalizationService localizationService ,
AppCaches appCaches )
2018-06-29 19:52:40 +02:00
{
2020-06-15 13:07:49 +02:00
_treeService = treeService ? ? throw new ArgumentNullException ( nameof ( treeService ) ) ;
_treeSearcher = treeSearcher ? ? throw new ArgumentNullException ( nameof ( treeSearcher ) ) ;
_searchableTreeCollection = searchableTreeCollection ? ?
throw new ArgumentNullException ( nameof ( searchableTreeCollection ) ) ;
_publishedContentQuery =
publishedContentQuery ? ? throw new ArgumentNullException ( nameof ( publishedContentQuery ) ) ;
_shortStringHelper = shortStringHelper ? ? throw new ArgumentNullException ( nameof ( shortStringHelper ) ) ;
_entityService = entityService ? ? throw new ArgumentNullException ( nameof ( entityService ) ) ;
2020-09-22 10:01:00 +02:00
_backofficeSecurityAccessor = backofficeSecurityAccessor ? ? throw new ArgumentNullException ( nameof ( backofficeSecurityAccessor ) ) ;
2020-06-15 13:07:49 +02:00
_publishedUrlProvider =
publishedUrlProvider ? ? throw new ArgumentNullException ( nameof ( publishedUrlProvider ) ) ;
_contentService = contentService ? ? throw new ArgumentNullException ( nameof ( contentService ) ) ;
_umbracoMapper = umbracoMapper ? ? throw new ArgumentNullException ( nameof ( umbracoMapper ) ) ;
_dataTypeService = dataTypeService ? ? throw new ArgumentNullException ( nameof ( dataTypeService ) ) ;
_sqlContext = sqlContext ? ? throw new ArgumentNullException ( nameof ( sqlContext ) ) ;
_localizedTextService =
localizedTextService ? ? throw new ArgumentNullException ( nameof ( localizedTextService ) ) ;
_fileService = fileService ? ? throw new ArgumentNullException ( nameof ( fileService ) ) ;
_contentTypeService = contentTypeService ? ? throw new ArgumentNullException ( nameof ( contentTypeService ) ) ;
_mediaTypeService = mediaTypeService ? ? throw new ArgumentNullException ( nameof ( mediaTypeService ) ) ;
_macroService = macroService ? ? throw new ArgumentNullException ( nameof ( macroService ) ) ;
_userService = userService ? ? throw new ArgumentNullException ( nameof ( userService ) ) ;
_localizationService = localizationService ? ? throw new ArgumentNullException ( nameof ( localizationService ) ) ;
2021-03-05 15:36:27 +01:00
_appCaches = appCaches ? ? throw new ArgumentNullException ( nameof ( appCaches ) ) ;
2018-06-29 19:52:40 +02:00
}
/// <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 )
{
2020-06-15 13:07:49 +02:00
var returnValue = string . IsNullOrWhiteSpace ( value ) ? string . Empty : value . ToSafeAlias ( _shortStringHelper , camelCase ) ;
2018-06-29 19:52:40 +02:00
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>
2019-07-03 08:33:30 +02:00
/// <param name="dataTypeKey">If set used to look up whether user and group start node permissions will be ignored.</param>
2018-06-29 19:52:40 +02:00
/// <returns></returns>
[HttpGet]
2019-07-03 08:33:30 +02:00
public IEnumerable < EntityBasic > Search ( string query , UmbracoEntityTypes type , string searchFrom = null , Guid ? dataTypeKey = null )
2018-06-29 19:52:40 +02:00
{
2019-06-28 13:03: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
2018-06-29 19:52:40 +02:00
if ( string . IsNullOrEmpty ( query ) )
return Enumerable . Empty < EntityBasic > ( ) ;
2019-02-04 13:42:07 +11:00
//TODO: This uses the internal UmbracoTreeSearcher, this instead should delgate to the ISearchableTree implementation for the type
2020-06-15 13:07:49 +02:00
var ignoreUserStartNodes = dataTypeKey . HasValue & & _dataTypeService . IsDataTypeIgnoringUserStartNodes ( dataTypeKey . Value ) ;
2019-06-28 13:03:36 +10:00
return ExamineSearch ( query , type , searchFrom , ignoreUserStartNodes ) ;
2018-06-29 19:52:40 +02:00
}
/// <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 ;
2020-10-21 16:51:00 +11:00
var allowedSections = _backofficeSecurityAccessor . BackOfficeSecurity . CurrentUser . AllowedSections . ToArray ( ) ;
2018-06-29 19:52:40 +02:00
2019-05-31 20:29:00 +02:00
foreach ( var searchableTree in _searchableTreeCollection . SearchableApplicationTrees . OrderBy ( t = > t . Value . SortOrder ) )
2018-06-29 19:52:40 +02:00
{
if ( allowedSections . Contains ( searchableTree . Value . AppAlias ) )
{
2019-01-17 13:20:19 +11:00
var tree = _treeService . GetByAlias ( searchableTree . Key ) ;
2018-06-29 19:52:40 +02:00
if ( tree = = null ) continue ; //shouldn't occur
2020-06-15 13:07:49 +02:00
result [ Tree . GetRootNodeDisplayName ( tree , _localizedTextService ) ] = new TreeSearchResult
2018-06-29 19:52:40 +02:00
{
2018-10-26 14:18:42 +11:00
Results = searchableTree . Value . SearchableTree . Search ( query , 200 , 0 , out var total ) ,
2018-06-29 19:52:40 +02:00
TreeAlias = searchableTree . Key ,
AppAlias = searchableTree . Value . AppAlias ,
2019-05-31 20:29:00 +02:00
JsFormatterService = searchableTree . Value . FormatterService ,
JsFormatterMethod = searchableTree . Value . FormatterMethod
2018-06-29 19:52:40 +02:00
} ;
}
}
return result ;
}
/// <summary>
/// Gets the path for a given node ID
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
2021-01-14 19:41:32 +01:00
public IConvertToActionResult GetPath ( int id , UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
2021-01-14 19:41:32 +01:00
var foundContentResult = GetResultForId ( id , type ) ;
var foundContent = foundContentResult . Value ;
if ( foundContent is null )
{
return foundContentResult ;
}
2018-06-29 19:52:40 +02:00
2021-09-14 22:13:39 +02:00
return new ActionResult < IEnumerable < int > > ( foundContent . Path . Split ( Constants . CharArrays . Comma , StringSplitOptions . RemoveEmptyEntries ) . Select (
s = > int . Parse ( s , CultureInfo . InvariantCulture ) ) ) ;
2018-06-29 19:52:40 +02:00
}
/// <summary>
/// Gets the path for a given node ID
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
2021-01-14 19:41:32 +01:00
public IConvertToActionResult GetPath ( Guid id , UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
2021-01-14 19:41:32 +01:00
var foundContentResult = GetResultForKey ( id , type ) ;
var foundContent = foundContentResult . Value ;
if ( foundContent is null )
{
return foundContentResult ;
}
2018-06-29 19:52:40 +02:00
2021-09-14 22:13:39 +02:00
return new ActionResult < IEnumerable < int > > ( foundContent . Path . Split ( Constants . CharArrays . Comma , StringSplitOptions . RemoveEmptyEntries ) . Select (
s = > int . Parse ( s , CultureInfo . InvariantCulture ) ) ) ;
2018-06-29 19:52:40 +02:00
}
/// <summary>
/// Gets the path for a given node ID
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
2021-01-14 19:41:32 +01:00
public IActionResult GetPath ( Udi id , UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
var guidUdi = id as GuidUdi ;
if ( guidUdi ! = null )
{
2021-01-14 19:41:32 +01:00
return GetPath ( guidUdi . Guid , type ) . Convert ( ) ;
2018-06-29 19:52:40 +02:00
}
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
2020-01-17 13:58:18 +01:00
/// <summary>
2020-10-05 20:48:38 +02:00
/// Gets the URL of an entity
2020-01-17 13:58:18 +01:00
/// </summary>
2021-07-22 11:10:02 +02:00
/// <param name="id">UDI of the entity to fetch URL for</param>
2020-01-17 13:58:18 +01:00
/// <param name="culture">The culture to fetch the URL for</param>
/// <returns>The URL or path to the item</returns>
2021-07-22 11:10:02 +02:00
public IActionResult GetUrl ( Udi id , string culture = "*" )
2020-01-17 13:58:18 +01:00
{
2021-07-22 11:10:02 +02:00
var intId = _entityService . GetId ( id ) ;
2020-01-17 13:58:18 +01:00
if ( ! intId . Success )
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2020-01-17 13:58:18 +01:00
UmbracoEntityTypes entityType ;
2021-07-22 11:10:02 +02:00
switch ( id . EntityType )
2020-01-17 13:58:18 +01:00
{
case Constants . UdiEntityType . Document :
entityType = UmbracoEntityTypes . Document ;
break ;
case Constants . UdiEntityType . Media :
entityType = UmbracoEntityTypes . Media ;
break ;
case Constants . UdiEntityType . Member :
entityType = UmbracoEntityTypes . Member ;
break ;
default :
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2020-01-17 13:58:18 +01:00
}
return GetUrl ( intId . Result , entityType , culture ) ;
}
2018-06-29 19:52:40 +02:00
/// <summary>
2020-10-05 20:48:38 +02:00
/// Gets the URL of an entity
2018-06-29 19:52:40 +02:00
/// </summary>
/// <param name="id">Int id of the entity to fetch URL for</param>
2019-01-26 10:52:19 -05:00
/// <param name="type">The type of entity such as Document, Media, Member</param>
2019-08-12 21:52:00 +02:00
/// <param name="culture">The culture to fetch the URL for</param>
2018-06-29 19:52:40 +02:00
/// <returns>The URL or path to the item</returns>
2019-06-28 13:03:36 +10:00
/// <remarks>
/// We are not restricting this with security because there is no sensitive data
/// </remarks>
2020-11-23 08:08:52 +01:00
public IActionResult GetUrl ( int id , UmbracoEntityTypes type , string culture = null )
2018-06-29 19:52:40 +02:00
{
2019-08-12 21:52:00 +02:00
culture = culture ? ? ClientCulture ( ) ;
2018-06-29 19:52:40 +02:00
var returnUrl = string . Empty ;
if ( type = = UmbracoEntityTypes . Document )
{
2020-06-15 13:07:49 +02:00
var foundUrl = _publishedUrlProvider . GetUrl ( id , culture : culture ) ;
2018-06-29 19:52:40 +02:00
if ( string . IsNullOrEmpty ( foundUrl ) = = false & & foundUrl ! = "#" )
{
returnUrl = foundUrl ;
2020-11-23 08:08:52 +01:00
return Ok ( returnUrl ) ;
2018-06-29 19:52:40 +02:00
}
}
2018-09-26 16:27:34 +02:00
var ancestors = GetResultForAncestors ( id , type ) ;
2018-06-29 19:52:40 +02:00
//if content, skip the first node for replicating NiceUrl defaults
2020-03-03 11:59:17 +01:00
if ( type = = UmbracoEntityTypes . Document )
{
2018-06-29 19:52:40 +02:00
ancestors = ancestors . Skip ( 1 ) ;
}
returnUrl = "/" + string . Join ( "/" , ancestors . Select ( x = > x . Name ) ) ;
2020-11-23 08:08:52 +01:00
return Ok ( returnUrl ) ;
2018-06-29 19:52:40 +02:00
}
2019-01-23 10:22:29 +01:00
2018-06-29 19:52:40 +02:00
/// <summary>
/// Gets an entity by a xpath query
/// </summary>
/// <param name="query"></param>
/// <param name="nodeContextId"></param>
/// <param name="type"></param>
/// <returns></returns>
2021-01-12 16:32:24 +01:00
public ActionResult < EntityBasic > GetByQuery ( string query , int nodeContextId , UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
2019-01-27 01:17:32 -05:00
// TODO: Rename this!!! It's misleading, it should be GetByXPath
2018-06-29 19:52:40 +02:00
if ( type ! = UmbracoEntityTypes . Document )
2019-01-26 10:52:19 -05:00
throw new ArgumentException ( "Get by query is only compatible with entities of type Document" ) ;
2018-06-29 19:52:40 +02:00
var q = ParseXPathQuery ( query , nodeContextId ) ;
2020-03-03 11:59:17 +01:00
var node = _publishedContentQuery . ContentSingleAtXPath ( q ) ;
2018-06-29 19:52:40 +02:00
if ( node = = null )
return null ;
return GetById ( node . Id , type ) ;
}
2019-01-26 10:52:19 -05:00
// PP: Work in progress on the query parser
2018-06-29 19:52:40 +02:00
private string ParseXPathQuery ( string query , int id )
{
return UmbracoXPathPathSyntaxParser . ParseXPathQuery (
xpathExpression : query ,
nodeContextId : id ,
getPath : nodeid = >
{
2020-06-15 13:07:49 +02:00
var ent = _entityService . Get ( nodeid ) ;
2021-01-22 15:02:25 +13:00
return ent . Path . Split ( Constants . CharArrays . Comma ) . Reverse ( ) ;
2018-06-29 19:52:40 +02:00
} ,
2020-03-03 11:59:17 +01:00
publishedContentExists : i = > _publishedContentQuery . Content ( i ) ! = null ) ;
2018-06-29 19:52:40 +02:00
}
2019-06-28 13:03:36 +10:00
[HttpGet]
2020-12-29 12:55:48 +01:00
public ActionResult < UrlAndAnchors > GetUrlAndAnchors ( Udi id , string culture = "*" )
2019-07-13 14:49:05 +02:00
{
2020-06-15 13:07:49 +02:00
var intId = _entityService . GetId ( id ) ;
2019-07-15 16:18:04 +10:00
if ( ! intId . Success )
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2019-06-24 10:52:35 +02:00
2019-07-15 16:18:04 +10:00
return GetUrlAndAnchors ( intId . Result , culture ) ;
}
2019-06-24 10:52:35 +02:00
[HttpGet]
2019-07-15 16:18:04 +10:00
public UrlAndAnchors GetUrlAndAnchors ( int id , string culture = "*" )
2019-06-28 13:03:36 +10:00
{
2020-01-17 13:58:18 +01:00
culture = culture ? ? ClientCulture ( ) ;
2020-06-15 13:07:49 +02:00
var url = _publishedUrlProvider . GetUrl ( id , culture : culture ) ;
var anchorValues = _contentService . GetAnchorValuesFromRTEs ( id , culture ) ;
2019-06-28 13:03:36 +10:00
return new UrlAndAnchors ( url , anchorValues ) ;
}
2019-07-02 11:17:23 +02:00
[HttpGet]
[HttpPost]
public IEnumerable < string > GetAnchors ( AnchorsModel model )
{
2020-06-15 13:07:49 +02:00
var anchorValues = _contentService . GetAnchorValuesFromRTEContent ( model . RteContent ) ;
2019-07-02 11:17:23 +02:00
return anchorValues ;
}
2019-06-28 13:03:36 +10:00
2018-06-29 19:52:40 +02:00
#region GetById
/// <summary>
/// Gets an entity by it's id
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
2021-01-12 16:32:24 +01:00
public ActionResult < EntityBasic > GetById ( int id , UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
2021-01-12 16:32:24 +01:00
return GetResultForId ( id , type ) ;
2018-06-29 19:52:40 +02:00
}
/// <summary>
/// Gets an entity by it's key
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
2021-01-12 16:32:24 +01:00
public ActionResult < EntityBasic > GetById ( Guid id , UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
2021-01-12 16:32:24 +01:00
return GetResultForKey ( id , type ) ;
2018-06-29 19:52:40 +02:00
}
/// <summary>
/// Gets an entity by it's UDI
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
2020-12-29 12:55:48 +01:00
public ActionResult < EntityBasic > GetById ( Udi id , UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
var guidUdi = id as GuidUdi ;
if ( guidUdi ! = null )
{
return GetResultForKey ( guidUdi . Guid , type ) ;
}
2020-12-29 12:55:48 +01:00
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
#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]
2021-01-29 10:30:28 +01:00
public ActionResult < IEnumerable < EntityBasic > > GetByIds ( [ FromJsonPath ] int [ ] ids , [ FromQuery ] UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
if ( ids = = null )
{
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
2020-12-29 12:55:48 +01:00
return new ActionResult < IEnumerable < EntityBasic > > ( GetResultForIds ( ids , type ) ) ;
2018-06-29 19:52:40 +02:00
}
/// <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]
2021-01-29 10:30:28 +01:00
public ActionResult < IEnumerable < EntityBasic > > GetByIds ( [ FromJsonPath ] Guid [ ] ids , [ FromQuery ] UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
if ( ids = = null )
{
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
2020-12-29 12:55:48 +01:00
return new ActionResult < IEnumerable < EntityBasic > > ( GetResultForKeys ( ids , type ) ) ;
2018-06-29 19:52:40 +02:00
}
/// <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]
2020-12-29 12:55:48 +01:00
public ActionResult < IEnumerable < EntityBasic > > GetByIds ( [ FromJsonPath ] Udi [ ] ids , [ FromQuery ] UmbracoEntityTypes type )
2018-06-29 19:52:40 +02:00
{
if ( ids = = null )
{
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
if ( ids . Length = = 0 )
{
2020-12-29 12:55:48 +01:00
return Enumerable . Empty < EntityBasic > ( ) . ToList ( ) ;
2018-06-29 19:52:40 +02:00
}
//all udi types will need to be the same in this list so we'll determine by the first
2021-08-06 11:59:41 +02:00
//currently we only support GuidUdi for this method
2018-06-29 19:52:40 +02:00
var guidUdi = ids [ 0 ] as GuidUdi ;
if ( guidUdi ! = null )
{
2020-12-29 12:55:48 +01:00
return new ActionResult < IEnumerable < EntityBasic > > ( GetResultForKeys ( ids . Select ( x = > ( ( GuidUdi ) x ) . Guid ) . ToArray ( ) , type ) ) ;
2018-06-29 19:52:40 +02:00
}
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
#endregion
2019-07-03 08:33:30 +02:00
public IEnumerable < EntityBasic > GetChildren ( int id , UmbracoEntityTypes type , Guid ? dataTypeKey = null )
2018-06-29 19:52:40 +02:00
{
2019-06-28 13:03:36 +10:00
var objectType = ConvertToObjectType ( type ) ;
if ( objectType . HasValue )
{
//TODO: Need to check for Object types that support hierarchy here, some might not.
2019-07-02 11:17:23 +02:00
var startNodes = GetStartNodes ( type ) ;
2019-06-28 13:03:36 +10:00
2019-07-03 08:33:30 +02:00
var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes ( dataTypeKey ) ;
2019-06-28 13:03:36 +10:00
// 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
2019-11-05 13:45:42 +01:00
if ( id = = Constants . System . Root & & startNodes . Length > 0 & & startNodes . Contains ( Constants . System . Root ) = = false & & ! ignoreUserStartNodes )
2019-06-28 13:03:36 +10:00
{
2020-06-15 13:07:49 +02:00
var nodes = _entityService . GetAll ( objectType . Value , startNodes ) . ToArray ( ) ;
2019-06-28 13:03:36 +10:00
if ( nodes . Length = = 0 )
return Enumerable . Empty < EntityBasic > ( ) ;
2020-06-15 13:07:49 +02:00
var pr = new List < EntityBasic > ( nodes . Select ( _umbracoMapper . Map < EntityBasic > ) ) ;
2019-06-28 13:03:36 +10:00
return pr ;
}
// else proceed as usual
2020-06-15 13:07:49 +02:00
return _entityService . GetChildren ( id , objectType . Value )
2019-06-28 13:03:36 +10:00
. WhereNotNull ( )
2020-06-15 13:07:49 +02:00
. Select ( _umbracoMapper . Map < EntityBasic > ) ;
2019-06-28 13:03:36 +10:00
}
//now we need to convert the unknown ones
switch ( type )
{
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 ) ;
}
2018-06-29 19:52:40 +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>
2019-11-08 14:26:06 +11:00
/// <param name="dataTypeKey"></param>
2018-06-29 19:52:40 +02:00
/// <returns></returns>
2020-12-29 12:55:48 +01:00
public ActionResult < PagedResult < EntityBasic > > GetPagedChildren (
2018-06-29 19:52:40 +02:00
string id ,
UmbracoEntityTypes type ,
int pageNumber ,
int pageSize ,
string orderBy = "SortOrder" ,
Direction orderDirection = Direction . Ascending ,
2019-06-28 13:03:36 +10:00
string filter = "" ,
2019-07-03 08:33:30 +02:00
Guid ? dataTypeKey = null )
2018-06-29 19:52:40 +02:00
{
2021-09-15 13:40:08 +02:00
if ( int . TryParse ( id , NumberStyles . Integer , CultureInfo . InvariantCulture , out var intId ) )
2018-06-29 19:52:40 +02:00
{
return GetPagedChildren ( intId , type , pageNumber , pageSize , orderBy , orderDirection , filter ) ;
}
2018-12-03 22:10:56 +11:00
if ( Guid . TryParse ( id , out _ ) )
2018-06-29 19:52:40 +02:00
{
//Not supported currently
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
2019-11-13 16:33:40 +11:00
if ( UdiParser . TryParse ( id , out _ ) )
2018-06-29 19:52:40 +02:00
{
//Not supported currently
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
//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-07-03 08:33:30 +02:00
return GetPagedChildren ( intId , type , pageNumber , pageSize , orderBy , orderDirection , filter , dataTypeKey ) ;
2018-06-29 19:52:40 +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
2019-02-04 13:42:07 +11:00
//TODO: We should really fix this in the EntityService but if we don't we should allow the ISearchableTree for the members controller
// to be used for this search instead of the built in/internal searcher
2021-06-21 21:47:40 +02:00
var searchResult = _treeSearcher . ExamineSearch ( filter ? ? "" , type , pageSize , pageNumber - 1 , out long total , null , id ) ;
2018-06-29 19:52:40 +02:00
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>
2020-12-29 12:55:48 +01:00
public ActionResult < PagedResult < EntityBasic > > GetPagedChildren (
2018-06-29 19:52:40 +02:00
int id ,
UmbracoEntityTypes type ,
int pageNumber ,
int pageSize ,
string orderBy = "SortOrder" ,
Direction orderDirection = Direction . Ascending ,
2019-06-28 13:03:36 +10:00
string filter = "" ,
2019-07-03 08:33:30 +02:00
Guid ? dataTypeKey = null )
2018-06-29 19:52:40 +02:00
{
if ( pageNumber < = 0 )
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
if ( pageSize < = 0 )
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
var objectType = ConvertToObjectType ( type ) ;
if ( objectType . HasValue )
{
2019-06-28 13:24:13 +10:00
IEnumerable < IEntitySlim > entities ;
2019-06-28 13:03:36 +10:00
long totalRecords ;
2019-07-02 11:17:23 +02:00
var startNodes = GetStartNodes ( type ) ;
2019-06-28 13:03:36 +10:00
2019-07-03 08:33:30 +02:00
var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes ( dataTypeKey ) ;
2019-06-28 13:03:36 +10:00
// 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
2019-11-05 13:45:42 +01:00
if ( id = = Constants . System . Root & & startNodes . Length > 0 & & startNodes . Contains ( Constants . System . Root ) = = false & & ! ignoreUserStartNodes )
2019-06-28 13:03:36 +10:00
{
if ( pageNumber > 0 )
return new PagedResult < EntityBasic > ( 0 , 0 , 0 ) ;
2020-06-15 13:07:49 +02:00
var nodes = _entityService . GetAll ( objectType . Value , startNodes ) . ToArray ( ) ;
2019-06-28 13:03:36 +10:00
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 )
{
2020-06-15 13:07:49 +02:00
Items = nodes . Select ( _umbracoMapper . Map < EntityBasic > )
2019-06-28 13:03:36 +10:00
} ;
return pr ;
}
// else proceed as usual
2020-06-15 13:07:49 +02:00
entities = _entityService . GetPagedChildren ( id , objectType . Value , pageNumber - 1 , pageSize , out totalRecords ,
2019-01-02 20:57:18 +01:00
filter . IsNullOrWhiteSpace ( )
? null
2020-06-15 13:07:49 +02:00
: _sqlContext . Query < IUmbracoEntity > ( ) . Where ( x = > x . Name . Contains ( filter ) ) ,
2018-12-21 13:15:46 +11:00
Ordering . By ( orderBy , orderDirection ) ) ;
2018-06-29 19:52:40 +02:00
2019-06-28 13:03:36 +10:00
2018-06-29 19:52:40 +02:00
if ( totalRecords = = 0 )
{
return new PagedResult < EntityBasic > ( 0 , 0 , 0 ) ;
}
2019-02-09 17:50:29 +01:00
var culture = ClientCulture ( ) ;
2018-06-29 19:52:40 +02:00
var pagedResult = new PagedResult < EntityBasic > ( totalRecords , pageNumber , pageSize )
{
2019-03-26 10:39:50 +01:00
Items = entities . Select ( source = >
{
2020-06-15 13:07:49 +02:00
var target = _umbracoMapper . Map < IEntitySlim , EntityBasic > ( source , context = >
2019-03-26 10:39:50 +01:00
{
context . SetCulture ( culture ) ;
2020-06-15 13:07:49 +02:00
context . SetCulture ( culture ) ;
2019-03-26 10:39:50 +01:00
} ) ;
2019-07-01 18:23:36 +10:00
//TODO: Why is this here and not in the mapping?
2019-03-26 10:39:50 +01:00
target . AdditionalData [ "hasChildren" ] = source . HasChildren ;
return target ;
} )
2018-06-29 19:52:40 +02:00
} ;
return pagedResult ;
}
//now we need to convert the unknown ones
switch ( type )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
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-07-02 10:56:44 +02:00
private int [ ] GetStartNodes ( UmbracoEntityTypes type )
{
switch ( type )
{
case UmbracoEntityTypes . Document :
2021-03-05 15:36:27 +01:00
return _backofficeSecurityAccessor . BackOfficeSecurity . CurrentUser . CalculateContentStartNodeIds ( _entityService , _appCaches ) ;
2019-07-02 10:56:44 +02:00
case UmbracoEntityTypes . Media :
2021-03-05 15:36:27 +01:00
return _backofficeSecurityAccessor . BackOfficeSecurity . CurrentUser . CalculateMediaStartNodeIds ( _entityService , _appCaches ) ;
2019-07-02 10:56:44 +02:00
default :
2020-03-03 11:59:17 +01:00
return Array . Empty < int > ( ) ;
2019-07-02 10:56:44 +02:00
}
}
2020-12-29 12:55:48 +01:00
public ActionResult < PagedResult < EntityBasic > > GetPagedDescendants (
2018-06-29 19:52:40 +02:00
int id ,
UmbracoEntityTypes type ,
int pageNumber ,
int pageSize ,
string orderBy = "SortOrder" ,
Direction orderDirection = Direction . Ascending ,
2019-06-28 13:03:36 +10:00
string filter = "" ,
2019-07-03 08:33:30 +02:00
Guid ? dataTypeKey = null )
2018-06-29 19:52:40 +02:00
{
if ( pageNumber < = 0 )
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
if ( pageSize < = 0 )
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
2019-11-05 15:05:51 +11:00
// re-normalize since NULL can be passed in
filter = filter ? ? string . Empty ;
2018-06-29 19:52:40 +02:00
var objectType = ConvertToObjectType ( type ) ;
if ( objectType . HasValue )
{
IEnumerable < IUmbracoEntity > entities ;
long totalRecords ;
2019-11-05 13:45:42 +01:00
if ( id = = Constants . System . Root )
2018-06-29 19:52:40 +02:00
{
// root is special: we reduce it to start nodes
2019-07-02 10:56:44 +02:00
int [ ] aids = GetStartNodes ( type ) ;
2018-06-29 19:52:40 +02:00
2019-07-03 08:33:30 +02:00
var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes ( dataTypeKey ) ;
2019-11-05 13:45:42 +01:00
entities = aids = = null | | aids . Contains ( Constants . System . Root ) | | ignoreUserStartNodes
2020-06-15 13:07:49 +02:00
? _entityService . GetPagedDescendants ( objectType . Value , pageNumber - 1 , pageSize , out totalRecords ,
_sqlContext . Query < IUmbracoEntity > ( ) . Where ( x = > x . Name . Contains ( filter ) ) ,
2018-12-21 13:15:46 +11:00
Ordering . By ( orderBy , orderDirection ) , includeTrashed : false )
2020-06-15 13:07:49 +02:00
: _entityService . GetPagedDescendants ( aids , objectType . Value , pageNumber - 1 , pageSize , out totalRecords ,
_sqlContext . Query < IUmbracoEntity > ( ) . Where ( x = > x . Name . Contains ( filter ) ) ,
2018-12-21 13:15:46 +11:00
Ordering . By ( orderBy , orderDirection ) ) ;
2018-06-29 19:52:40 +02:00
}
else
{
2020-06-15 13:07:49 +02:00
entities = _entityService . GetPagedDescendants ( id , objectType . Value , pageNumber - 1 , pageSize , out totalRecords ,
_sqlContext . Query < IUmbracoEntity > ( ) . Where ( x = > x . Name . Contains ( filter ) ) ,
2018-12-21 13:15:46 +11:00
Ordering . By ( orderBy , orderDirection ) ) ;
2018-06-29 19:52:40 +02:00
}
if ( totalRecords = = 0 )
{
return new PagedResult < EntityBasic > ( 0 , 0 , 0 ) ;
}
var pagedResult = new PagedResult < EntityBasic > ( totalRecords , pageNumber , pageSize )
{
2019-02-09 15:41:30 +01:00
Items = entities . Select ( MapEntities ( ) )
2018-06-29 19:52:40 +02:00
} ;
return pagedResult ;
}
//now we need to convert the unknown ones
switch ( type )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
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 ) ;
}
}
2020-06-15 13:07:49 +02:00
private bool IsDataTypeIgnoringUserStartNodes ( Guid ? dataTypeKey ) = > dataTypeKey . HasValue & & _dataTypeService . IsDataTypeIgnoringUserStartNodes ( dataTypeKey . Value ) ;
2019-06-28 13:03:36 +10:00
2020-06-15 13:07:49 +02:00
public IEnumerable < EntityBasic > GetAncestors ( int id , UmbracoEntityTypes type , [ ModelBinder ( typeof ( HttpQueryStringModelBinder ) ) ] FormCollection queryStrings )
2018-06-29 19:52:40 +02:00
{
2019-06-18 19:18:48 +02:00
return GetResultForAncestors ( id , type , queryStrings ) ;
2018-06-29 19:52:40 +02:00
}
2021-09-08 14:59:52 +02:00
public ActionResult < IEnumerable < EntityBasic > > GetAncestors ( Guid id , UmbracoEntityTypes type , [ ModelBinder ( typeof ( HttpQueryStringModelBinder ) ) ] FormCollection queryStrings )
{
var entity = _entityService . Get ( id ) ;
if ( entity is null )
{
return NotFound ( ) ;
}
return Ok ( GetResultForAncestors ( entity . Id , type , queryStrings ) ) ;
}
2018-06-29 19:52:40 +02:00
/// <summary>
/// Searches for results based on the entity type
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="searchFrom"></param>
2019-06-28 13:03:36 +10:00
/// <param name="ignoreUserStartNodes">If set to true, user and group start node permissions will be ignored.</param>
2018-06-29 19:52:40 +02:00
/// <returns></returns>
2019-06-28 13:03:36 +10:00
private IEnumerable < SearchResultEntity > ExamineSearch ( string query , UmbracoEntityTypes entityType , string searchFrom = null , bool ignoreUserStartNodes = false )
2018-06-29 19:52:40 +02:00
{
2020-02-26 16:16:26 +01:00
var culture = ClientCulture ( ) ;
2020-03-10 07:44:22 +01:00
return _treeSearcher . ExamineSearch ( query , entityType , 200 , 0 , out _ , culture , searchFrom , ignoreUserStartNodes ) ;
2018-06-29 19:52:40 +02:00
}
private IEnumerable < EntityBasic > GetResultForChildren ( int id , UmbracoEntityTypes entityType )
{
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
2019-01-27 01:17:32 -05:00
// TODO: Need to check for Object types that support hierarchic here, some might not.
2018-06-29 19:52:40 +02:00
2020-06-15 13:07:49 +02:00
return _entityService . GetChildren ( id , objectType . Value )
2018-06-29 19:52:40 +02:00
. WhereNotNull ( )
2019-02-09 15:41:30 +01:00
. Select ( MapEntities ( ) ) ;
2018-06-29 19:52:40 +02:00
}
//now we need to convert the unknown ones
switch ( entityType )
{
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 ) ;
}
}
2020-06-15 13:07:49 +02:00
private IEnumerable < EntityBasic > GetResultForAncestors ( int id , UmbracoEntityTypes entityType , FormCollection queryStrings = null )
2018-06-29 19:52:40 +02:00
{
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
2019-01-27 01:17:32 -05:00
// TODO: Need to check for Object types that support hierarchic here, some might not.
2018-06-29 19:52:40 +02:00
2021-09-14 22:13:39 +02:00
var ids = _entityService . Get ( id ) . Path . Split ( Constants . CharArrays . Comma ) . Select ( s = > int . Parse ( s , CultureInfo . InvariantCulture ) ) . Distinct ( ) . ToArray ( ) ;
2018-06-29 19:52:40 +02:00
2019-06-28 13:24:13 +10:00
var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes ( queryStrings ? . GetValue < Guid ? > ( "dataTypeId" ) ) ;
2019-06-28 13:03:36 +10:00
if ( ignoreUserStartNodes = = false )
2018-06-29 19:52:40 +02:00
{
2019-06-28 13:03:36 +10:00
int [ ] aids = null ;
switch ( entityType )
{
case UmbracoEntityTypes . Document :
2021-03-05 15:36:27 +01:00
aids = _backofficeSecurityAccessor . BackOfficeSecurity . CurrentUser . CalculateContentStartNodeIds ( _entityService , _appCaches ) ;
2019-06-28 13:03:36 +10:00
break ;
case UmbracoEntityTypes . Media :
2021-03-05 15:36:27 +01:00
aids = _backofficeSecurityAccessor . BackOfficeSecurity . CurrentUser . CalculateMediaStartNodeIds ( _entityService , _appCaches ) ;
2019-06-28 13:03:36 +10:00
break ;
}
2018-06-29 19:52:40 +02:00
2019-06-28 13:03:36 +10:00
if ( aids ! = null )
2018-06-29 19:52:40 +02:00
{
2019-06-28 13:03:36 +10:00
var lids = new List < int > ( ) ;
var ok = false ;
foreach ( var i in ids )
2018-06-29 19:52:40 +02:00
{
2019-06-28 13:03:36 +10:00
if ( ok )
{
lids . Add ( i ) ;
continue ;
}
if ( aids . Contains ( i ) )
{
lids . Add ( i ) ;
ok = true ;
}
2018-06-29 19:52:40 +02:00
}
2019-06-28 13:03:36 +10:00
ids = lids . ToArray ( ) ;
2018-06-29 19:52:40 +02:00
}
}
2018-09-26 16:27:34 +02:00
var culture = queryStrings ? . GetValue < string > ( "culture" ) ;
2018-06-29 19:52:40 +02:00
return ids . Length = = 0
? Enumerable . Empty < EntityBasic > ( )
2020-06-15 13:07:49 +02:00
: _entityService . GetAll ( objectType . Value , ids )
2018-06-29 19:52:40 +02:00
. WhereNotNull ( )
. OrderBy ( x = > x . Level )
2019-02-09 15:41:30 +01:00
. Select ( MapEntities ( culture ) ) ;
2018-06-29 19:52:40 +02:00
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
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 > GetResultForKeys ( Guid [ ] keys , UmbracoEntityTypes entityType )
{
if ( keys . Length = = 0 )
return Enumerable . Empty < EntityBasic > ( ) ;
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
2020-06-15 13:07:49 +02:00
var entities = _entityService . GetAll ( objectType . Value , keys )
2018-06-29 19:52:40 +02:00
. WhereNotNull ( )
2019-02-09 15:41:30 +01:00
. Select ( MapEntities ( ) ) ;
2018-06-29 19:52:40 +02:00
// 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 . 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 )
{
2020-06-15 13:07:49 +02:00
var entities = _entityService . GetAll ( objectType . Value , ids )
2018-06-29 19:52:40 +02:00
. WhereNotNull ( )
2019-02-09 15:41:30 +01:00
. Select ( MapEntities ( ) ) ;
2018-06-29 19:52:40 +02:00
// 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 . Language :
case UmbracoEntityTypes . User :
case UmbracoEntityTypes . Macro :
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + entityType ) ;
}
}
2020-12-29 12:55:48 +01:00
private ActionResult < EntityBasic > GetResultForKey ( Guid key , UmbracoEntityTypes entityType )
2018-06-29 19:52:40 +02:00
{
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
2020-06-15 13:07:49 +02:00
var found = _entityService . Get ( key , objectType . Value ) ;
2018-06-29 19:52:40 +02:00
if ( found = = null )
{
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
2020-06-15 13:07:49 +02:00
return _umbracoMapper . Map < IEntitySlim , EntityBasic > ( found ) ;
2018-06-29 19:52:40 +02:00
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
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 ) ;
}
}
2020-12-29 12:55:48 +01:00
private ActionResult < EntityBasic > GetResultForId ( int id , UmbracoEntityTypes entityType )
2018-06-29 19:52:40 +02:00
{
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
2020-06-15 13:07:49 +02:00
var found = _entityService . Get ( id , objectType . Value ) ;
2018-06-29 19:52:40 +02:00
if ( found = = null )
{
2021-01-12 14:00:14 +01:00
return NotFound ( ) ;
2018-06-29 19:52:40 +02:00
}
2019-02-09 15:41:30 +01:00
return MapEntity ( found ) ;
2018-06-29 19:52:40 +02:00
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . PropertyType :
case UmbracoEntityTypes . PropertyGroup :
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 :
2019-05-09 07:21:18 +01:00
return UmbracoObjectTypes . MemberType ;
2018-06-29 19:52:40 +02:00
case UmbracoEntityTypes . MemberGroup :
return UmbracoObjectTypes . MemberGroup ;
case UmbracoEntityTypes . MediaType :
return UmbracoObjectTypes . MediaType ;
case UmbracoEntityTypes . DocumentType :
return UmbracoObjectTypes . DocumentType ;
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 ;
}
}
2019-01-23 10:22:29 +01:00
/// <summary>
///
/// </summary>
/// <param name="type">The type of entity.</param>
/// <param name="postFilter">Optional filter - Format like: "BoolVariable==true&IntVariable>=6". Invalid filters are ignored.</param>
/// <returns></returns>
public IEnumerable < EntityBasic > GetAll ( UmbracoEntityTypes type , string postFilter )
2018-06-29 19:52:40 +02:00
{
2019-01-23 10:22:29 +01:00
return GetResultForAll ( type , postFilter ) ;
2018-06-29 19:52:40 +02:00
}
/// <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>
/// <returns></returns>
2019-01-23 10:22:29 +01:00
private IEnumerable < EntityBasic > GetResultForAll ( UmbracoEntityTypes entityType , string postFilter = null )
2018-06-29 19:52:40 +02:00
{
var objectType = ConvertToObjectType ( entityType ) ;
if ( objectType . HasValue )
{
2019-01-27 01:17:32 -05:00
// TODO: Should we order this by something ?
2020-06-15 13:07:49 +02:00
var entities = _entityService . GetAll ( objectType . Value ) . WhereNotNull ( ) . Select ( MapEntities ( ) ) ;
2019-01-23 10:22:29 +01:00
return ExecutePostFilter ( entities , postFilter ) ;
2018-06-29 19:52:40 +02:00
}
//now we need to convert the unknown ones
switch ( entityType )
{
case UmbracoEntityTypes . Template :
2020-06-15 13:07:49 +02:00
var templates = _fileService . GetTemplates ( ) ;
2019-01-23 10:22:29 +01:00
var filteredTemplates = ExecutePostFilter ( templates , postFilter ) ;
2019-02-09 15:41:30 +01:00
return filteredTemplates . Select ( MapEntities ( ) ) ;
2018-06-29 19:52:40 +02:00
case UmbracoEntityTypes . Macro :
//Get all macros from the macro service
2020-06-15 13:07:49 +02:00
var macros = _macroService . GetAll ( ) . WhereNotNull ( ) . OrderBy ( x = > x . Name ) ;
2019-01-23 10:22:29 +01:00
var filteredMacros = ExecutePostFilter ( macros , postFilter ) ;
2019-02-09 15:41:30 +01:00
return filteredMacros . Select ( MapEntities ( ) ) ;
2018-06-29 19:52:40 +02:00
case UmbracoEntityTypes . PropertyType :
//get all document types, then combine all property types into one list
2020-06-15 13:07:49 +02:00
var propertyTypes = _contentTypeService . GetAll ( ) . Cast < IContentTypeComposition > ( )
. Concat ( _mediaTypeService . GetAll ( ) )
2018-06-29 19:52:40 +02:00
. ToArray ( )
. SelectMany ( x = > x . PropertyTypes )
. DistinctBy ( composition = > composition . Alias ) ;
2019-01-23 10:22:29 +01:00
var filteredPropertyTypes = ExecutePostFilter ( propertyTypes , postFilter ) ;
2020-06-15 13:07:49 +02:00
return _umbracoMapper . MapEnumerable < IPropertyType , EntityBasic > ( filteredPropertyTypes ) ;
2018-06-29 19:52:40 +02:00
case UmbracoEntityTypes . PropertyGroup :
//get all document types, then combine all property types into one list
2020-06-15 13:07:49 +02:00
var propertyGroups = _contentTypeService . GetAll ( ) . Cast < IContentTypeComposition > ( )
. Concat ( _mediaTypeService . GetAll ( ) )
2018-06-29 19:52:40 +02:00
. ToArray ( )
. SelectMany ( x = > x . PropertyGroups )
. DistinctBy ( composition = > composition . Name ) ;
2019-01-23 10:22:29 +01:00
var filteredpropertyGroups = ExecutePostFilter ( propertyGroups , postFilter ) ;
2020-06-15 13:07:49 +02:00
return _umbracoMapper . MapEnumerable < PropertyGroup , EntityBasic > ( filteredpropertyGroups ) ;
2018-06-29 19:52:40 +02:00
case UmbracoEntityTypes . User :
2020-06-15 13:07:49 +02:00
var users = _userService . GetAll ( 0 , int . MaxValue , out _ ) ;
2019-01-23 10:22:29 +01:00
var filteredUsers = ExecutePostFilter ( users , postFilter ) ;
2020-06-15 13:07:49 +02:00
return _umbracoMapper . MapEnumerable < IUser , EntityBasic > ( filteredUsers ) ;
2018-06-29 19:52:40 +02:00
2019-01-17 00:29:43 +11:00
case UmbracoEntityTypes . Stylesheet :
2019-01-23 10:22:29 +01:00
if ( ! postFilter . IsNullOrWhiteSpace ( ) )
2019-01-17 00:29:43 +11:00
throw new NotSupportedException ( "Filtering on stylesheets is not currently supported" ) ;
2020-06-15 13:07:49 +02:00
return _fileService . GetStylesheets ( ) . Select ( MapEntities ( ) ) ;
2019-01-23 10:22:29 +01:00
2021-07-08 13:11:39 -06:00
case UmbracoEntityTypes . Script :
if ( ! postFilter . IsNullOrWhiteSpace ( ) )
throw new NotSupportedException ( "Filtering on scripts is not currently supported" ) ;
return _fileService . GetScripts ( ) . Select ( MapEntities ( ) ) ;
case UmbracoEntityTypes . PartialView :
if ( ! postFilter . IsNullOrWhiteSpace ( ) )
2021-07-13 02:09:15 +10:00
throw new NotSupportedException ( "Filtering on partial views is not currently supported" ) ;
2021-07-08 13:11:39 -06:00
return _fileService . GetPartialViews ( ) . Select ( MapEntities ( ) ) ;
2018-06-29 19:52:40 +02:00
case UmbracoEntityTypes . Language :
2019-01-17 00:29:43 +11:00
2020-03-03 11:59:17 +01:00
if ( ! postFilter . IsNullOrWhiteSpace ( ) )
2019-01-17 00:29:43 +11:00
throw new NotSupportedException ( "Filtering on languages is not currently supported" ) ;
2020-06-15 13:07:49 +02:00
return _localizationService . GetAllLanguages ( ) . Select ( MapEntities ( ) ) ;
2019-01-17 00:29:43 +11:00
case UmbracoEntityTypes . DictionaryItem :
2019-01-23 10:22:29 +01:00
if ( ! postFilter . IsNullOrWhiteSpace ( ) )
throw new NotSupportedException ( "Filtering on dictionary items is not currently supported" ) ;
2019-01-17 00:29:43 +11:00
return GetAllDictionaryItems ( ) ;
2018-06-29 19:52:40 +02:00
default :
throw new NotSupportedException ( "The " + typeof ( EntityController ) + " does not currently support data for the type " + entityType ) ;
}
}
2019-01-23 10:22:29 +01:00
private IEnumerable < T > ExecutePostFilter < T > ( IEnumerable < T > entities , string postFilter )
2018-06-29 19:52:40 +02:00
{
2019-01-23 10:22:29 +01:00
if ( postFilter . IsNullOrWhiteSpace ( ) ) return entities ;
2021-01-22 15:02:25 +13:00
var postFilterConditions = postFilter . Split ( Constants . CharArrays . Ampersand ) ;
2019-01-23 10:22:29 +01:00
foreach ( var postFilterCondition in postFilterConditions )
2018-06-29 19:52:40 +02:00
{
2019-01-23 10:22:29 +01:00
var queryCondition = BuildQueryCondition < T > ( postFilterCondition ) ;
if ( queryCondition ! = null )
{
var whereClauseExpression = queryCondition . BuildCondition < T > ( "x" ) ;
entities = entities . Where ( whereClauseExpression . Compile ( ) ) ;
}
2018-06-29 19:52:40 +02:00
}
return entities ;
}
2019-01-17 00:29:43 +11:00
2021-01-22 15:02:25 +13:00
private static readonly string [ ] _postFilterSplitStrings = new [ ]
2021-08-06 11:59:41 +02:00
{
"=" ,
"==" ,
"!=" ,
"<>" ,
">" ,
"<" ,
">=" ,
"<="
} ;
2021-01-22 15:02:25 +13:00
private static QueryCondition BuildQueryCondition < T > ( string postFilter )
{
var postFilterParts = postFilter . Split ( _postFilterSplitStrings , 2 , StringSplitOptions . RemoveEmptyEntries ) ;
2019-01-23 10:22:29 +01:00
if ( postFilterParts . Length ! = 2 )
{
return null ;
}
var propertyName = postFilterParts [ 0 ] ;
var constraintValue = postFilterParts [ 1 ] ;
var stringOperator = postFilter . Substring ( propertyName . Length ,
postFilter . Length - propertyName . Length - constraintValue . Length ) ;
Operator binaryOperator ;
try
{
binaryOperator = OperatorFactory . FromString ( stringOperator ) ;
}
catch ( ArgumentException )
{
// unsupported operators are ignored
return null ;
}
var type = typeof ( T ) ;
var property = type . GetProperty ( propertyName , BindingFlags . Public | BindingFlags . Instance ) ;
if ( property = = null )
{
return null ;
}
var queryCondition = new QueryCondition ( )
{
Term = new OperatorTerm ( )
{
Operator = binaryOperator
} ,
ConstraintValue = constraintValue ,
Property = new PropertyModel ( )
{
Alias = propertyName ,
Name = propertyName ,
Type = property . PropertyType . Name
}
} ;
return queryCondition ;
}
2019-02-09 15:41:30 +01:00
private Func < object , EntityBasic > MapEntities ( string culture = null )
{
2019-02-09 17:50:29 +01:00
culture = culture ? ? ClientCulture ( ) ;
2019-02-09 15:41:30 +01:00
return x = > MapEntity ( x , culture ) ;
}
2019-01-23 10:22:29 +01:00
2019-02-09 15:41:30 +01:00
private EntityBasic MapEntity ( object entity , string culture = null )
{
2019-02-09 17:50:29 +01:00
culture = culture ? ? ClientCulture ( ) ;
2020-06-15 13:07:49 +02:00
return _umbracoMapper . Map < EntityBasic > ( entity , context = > { context . SetCulture ( culture ) ; } ) ;
2019-02-09 15:41:30 +01:00
}
2019-01-23 10:22:29 +01:00
2019-02-09 17:50:29 +01:00
private string ClientCulture ( ) = > Request . ClientCulture ( ) ;
2019-01-17 00:29:43 +11:00
#region Methods to get all dictionary items
private IEnumerable < EntityBasic > GetAllDictionaryItems ( )
{
var list = new List < EntityBasic > ( ) ;
2020-06-15 13:07:49 +02:00
foreach ( var dictionaryItem in _localizationService . GetRootDictionaryItems ( ) . OrderBy ( DictionaryItemSort ( ) ) )
2019-01-17 00:29:43 +11:00
{
2020-06-15 13:07:49 +02:00
var item = _umbracoMapper . Map < IDictionaryItem , EntityBasic > ( dictionaryItem ) ;
2019-01-17 00:29:43 +11:00
list . Add ( item ) ;
GetChildItemsForList ( dictionaryItem , list ) ;
}
return list ;
}
private static Func < IDictionaryItem , string > DictionaryItemSort ( ) = > item = > item . ItemKey ;
private void GetChildItemsForList ( IDictionaryItem dictionaryItem , ICollection < EntityBasic > list )
{
2020-06-15 13:07:49 +02:00
foreach ( var childItem in _localizationService . GetDictionaryItemChildren ( dictionaryItem . Key ) . OrderBy ( DictionaryItemSort ( ) ) )
2019-01-17 00:29:43 +11:00
{
2020-06-15 13:07:49 +02:00
var item = _umbracoMapper . Map < IDictionaryItem , EntityBasic > ( childItem ) ;
2019-01-17 00:29:43 +11:00
list . Add ( item ) ;
GetChildItemsForList ( childItem , list ) ;
}
2019-01-23 10:22:29 +01:00
}
2019-01-17 00:29:43 +11:00
#endregion
2018-06-29 19:52:40 +02:00
}
}