2021-09-15 13:40:08 +02:00
using System.Globalization ;
2020-09-21 13:04:57 +02:00
using Microsoft.Extensions.Logging ;
2021-02-18 11:06:02 +01:00
using Umbraco.Cms.Core ;
2022-05-11 12:55:42 +02:00
using Umbraco.Cms.Core.Actions ;
2021-03-05 15:36:27 +01:00
using Umbraco.Cms.Core.Cache ;
2021-02-18 11:06:02 +01:00
using Umbraco.Cms.Core.Dictionary ;
using Umbraco.Cms.Core.Mapping ;
using Umbraco.Cms.Core.Models ;
using Umbraco.Cms.Core.Models.ContentEditing ;
using Umbraco.Cms.Core.Models.Mapping ;
using Umbraco.Cms.Core.Models.Membership ;
using Umbraco.Cms.Core.Models.PublishedContent ;
using Umbraco.Cms.Core.Routing ;
using Umbraco.Cms.Core.Security ;
using Umbraco.Cms.Core.Services ;
using Umbraco.Cms.Core.Web ;
using Umbraco.Cms.Web.BackOffice.Trees ;
using Umbraco.Extensions ;
2017-07-19 13:42:47 +02:00
2022-06-20 08:37:17 +02:00
namespace Umbraco.Cms.Web.BackOffice.Mapping ;
/// <summary>
/// Declares how model mappings for content
/// </summary>
internal class ContentMapDefinition : IMapDefinition
2017-07-19 13:42:47 +02:00
{
2022-06-20 08:37:17 +02:00
private readonly AppCaches _appCaches ;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor ;
private readonly ContentBasicSavedStateMapper < ContentPropertyBasic > _basicStateMapper ;
private readonly CommonMapper _commonMapper ;
private readonly CommonTreeNodeMapper _commonTreeNodeMapper ;
private readonly IContentService _contentService ;
private readonly IContentTypeService _contentTypeService ;
private readonly ContentVariantMapper _contentVariantMapper ;
private readonly ICultureDictionary _cultureDictionary ;
private readonly IEntityService _entityService ;
private readonly IFileService _fileService ;
private readonly ILocalizationService _localizationService ;
private readonly ILocalizedTextService _localizedTextService ;
private readonly ILoggerFactory _loggerFactory ;
private readonly IPublishedRouter _publishedRouter ;
private readonly IPublishedUrlProvider _publishedUrlProvider ;
private readonly ContentSavedStateMapper < ContentPropertyDisplay > _stateMapper ;
private readonly TabsAndPropertiesMapper < IContent > _tabsAndPropertiesMapper ;
private readonly IUmbracoContextAccessor _umbracoContextAccessor ;
private readonly UriUtility _uriUtility ;
private readonly IUserService _userService ;
private readonly IVariationContextAccessor _variationContextAccessor ;
public ContentMapDefinition (
CommonMapper commonMapper ,
CommonTreeNodeMapper commonTreeNodeMapper ,
ICultureDictionary cultureDictionary ,
ILocalizedTextService localizedTextService ,
IContentService contentService ,
IContentTypeService contentTypeService ,
IFileService fileService ,
IUmbracoContextAccessor umbracoContextAccessor ,
IPublishedRouter publishedRouter ,
ILocalizationService localizationService ,
ILoggerFactory loggerFactory ,
IUserService userService ,
IVariationContextAccessor variationContextAccessor ,
IContentTypeBaseServiceProvider contentTypeBaseServiceProvider ,
UriUtility uriUtility ,
IPublishedUrlProvider publishedUrlProvider ,
IEntityService entityService ,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor ,
AppCaches appCaches )
2017-07-19 13:42:47 +02:00
{
2022-06-20 08:37:17 +02:00
_commonMapper = commonMapper ;
_commonTreeNodeMapper = commonTreeNodeMapper ;
_cultureDictionary = cultureDictionary ;
_localizedTextService = localizedTextService ;
_contentService = contentService ;
_contentTypeService = contentTypeService ;
_fileService = fileService ;
_umbracoContextAccessor = umbracoContextAccessor ;
_publishedRouter = publishedRouter ;
_localizationService = localizationService ;
_loggerFactory = loggerFactory ;
_userService = userService ;
_entityService = entityService ;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor ;
_variationContextAccessor = variationContextAccessor ;
_uriUtility = uriUtility ;
_publishedUrlProvider = publishedUrlProvider ;
_appCaches = appCaches ;
_tabsAndPropertiesMapper = new TabsAndPropertiesMapper < IContent > ( cultureDictionary , localizedTextService , contentTypeBaseServiceProvider ) ;
_stateMapper = new ContentSavedStateMapper < ContentPropertyDisplay > ( ) ;
_basicStateMapper = new ContentBasicSavedStateMapper < ContentPropertyBasic > ( ) ;
_contentVariantMapper = new ContentVariantMapper ( _localizationService , localizedTextService ) ;
}
2018-09-10 09:09:24 +01:00
2022-06-20 08:37:17 +02:00
public void DefineMaps ( IUmbracoMapper mapper )
{
2022-06-21 11:44:28 +02:00
mapper . Define < IContent , ContentItemBasic < ContentPropertyBasic > > ( ( source , context ) = > new ContentItemBasic < ContentPropertyBasic > ( ) , Map ) ;
2022-06-20 08:37:17 +02:00
mapper . Define < IContent , ContentPropertyCollectionDto > ( ( source , context ) = > new ContentPropertyCollectionDto ( ) , Map ) ;
2021-10-19 11:29:25 +01:00
2022-06-20 08:37:17 +02:00
mapper . Define < IContent , ContentItemDisplay > ( ( source , context ) = > new ContentItemDisplay ( ) , Map ) ;
2022-06-21 11:44:28 +02:00
mapper . Define < IContent , ContentItemDisplayWithSchedule > ( ( source , context ) = > new ContentItemDisplayWithSchedule ( ) , Map ) ;
2021-10-19 11:29:25 +01:00
2022-06-20 08:37:17 +02:00
mapper . Define < IContent , ContentVariantDisplay > ( ( source , context ) = > new ContentVariantDisplay ( ) , Map ) ;
mapper . Define < IContent , ContentVariantScheduleDisplay > ( ( source , context ) = > new ContentVariantScheduleDisplay ( ) , Map ) ;
2022-06-21 11:44:28 +02:00
mapper . Define < ContentItemDisplayWithSchedule , ContentItemDisplay > ( ( source , context ) = > new ContentItemDisplay ( ) , Map ) ;
mapper . Define < ContentItemDisplay , ContentItemDisplayWithSchedule > ( ( source , context ) = > new ContentItemDisplayWithSchedule ( ) , Map ) ;
mapper . Define < ContentVariantDisplay , ContentVariantScheduleDisplay > ( ( source , context ) = > new ContentVariantScheduleDisplay ( ) , Map ) ;
mapper . Define < ContentVariantScheduleDisplay , ContentVariantDisplay > ( ( source , context ) = > new ContentVariantDisplay ( ) , Map ) ;
}
// Umbraco.Code.MapAll
private void Map ( ContentVariantScheduleDisplay source , ContentVariantDisplay target , MapperContext context )
{
target . CreateDate = source . CreateDate ;
target . DisplayName = source . DisplayName ;
target . Language = source . Language ;
target . Name = source . Name ;
target . PublishDate = source . PublishDate ;
target . Segment = source . Segment ;
target . State = source . State ;
target . Tabs = source . Tabs ;
target . UpdateDate = source . UpdateDate ;
2022-07-04 15:07:59 +02:00
target . AllowedActions = source . AllowedActions ;
2022-06-21 11:44:28 +02:00
}
// Umbraco.Code.MapAll
private void Map ( ContentItemDisplay source , ContentItemDisplayWithSchedule target , MapperContext context )
{
2022-12-13 15:04:39 +01:00
foreach ( KeyValuePair < string , object > additionalData in source . AdditionalData )
{
target . AdditionalData . Add ( additionalData ) ;
}
2022-06-21 11:44:28 +02:00
target . AllowedActions = source . AllowedActions ;
target . AllowedTemplates = source . AllowedTemplates ;
target . AllowPreview = source . AllowPreview ;
target . ContentApps = source . ContentApps ;
target . ContentDto = source . ContentDto ;
target . ContentTypeAlias = source . ContentTypeAlias ;
target . ContentTypeId = source . ContentTypeId ;
target . ContentTypeKey = source . ContentTypeKey ;
target . ContentTypeName = source . ContentTypeName ;
target . DocumentType = source . DocumentType ;
target . Errors = source . Errors ;
target . Icon = source . Icon ;
target . Id = source . Id ;
target . IsBlueprint = source . IsBlueprint ;
target . IsChildOfListView = source . IsChildOfListView ;
target . IsContainer = source . IsContainer ;
target . IsElement = source . IsElement ;
target . Key = source . Key ;
target . Owner = source . Owner ;
target . ParentId = source . ParentId ;
target . Path = source . Path ;
target . PersistedContent = source . PersistedContent ;
target . SortOrder = source . SortOrder ;
target . TemplateAlias = source . TemplateAlias ;
target . TemplateId = source . TemplateId ;
target . Trashed = source . Trashed ;
target . TreeNodeUrl = source . TreeNodeUrl ;
target . Udi = source . Udi ;
target . UpdateDate = source . UpdateDate ;
target . Updater = source . Updater ;
target . Urls = source . Urls ;
target . Variants = context . MapEnumerable < ContentVariantDisplay , ContentVariantScheduleDisplay > ( source . Variants ) ;
}
// Umbraco.Code.MapAll
private void Map ( ContentVariantDisplay source , ContentVariantScheduleDisplay target , MapperContext context )
{
target . CreateDate = source . CreateDate ;
target . DisplayName = source . DisplayName ;
target . Language = source . Language ;
target . Name = source . Name ;
target . PublishDate = source . PublishDate ;
target . Segment = source . Segment ;
target . State = source . State ;
target . Tabs = source . Tabs ;
target . UpdateDate = source . UpdateDate ;
2022-07-04 15:07:59 +02:00
target . AllowedActions = source . AllowedActions ;
2022-06-21 11:44:28 +02:00
// We'll only try and map the ReleaseDate/ExpireDate if the "old" ContentVariantScheduleDisplay is in the context, otherwise we'll just skip it quietly.
_ = context . Items . TryGetValue ( nameof ( ContentItemDisplayWithSchedule . Variants ) , out var variants ) ;
if ( variants is IEnumerable < ContentVariantScheduleDisplay > scheduleDisplays )
{
ContentVariantScheduleDisplay ? item = scheduleDisplays . FirstOrDefault ( x = > x . Language ? . Id = = source . Language ? . Id & & x . Segment = = source . Segment ) ;
if ( item is null )
{
// If we can't find the old variants display, we'll just not try and map it.
return ;
}
target . ReleaseDate = item . ReleaseDate ;
target . ExpireDate = item . ExpireDate ;
}
}
// Umbraco.Code.MapAll
private static void Map ( ContentItemDisplayWithSchedule source , ContentItemDisplay target , MapperContext context )
{
2022-12-13 15:04:39 +01:00
foreach ( KeyValuePair < string , object > additionalData in source . AdditionalData )
{
target . AdditionalData . Add ( additionalData ) ;
}
2022-06-21 11:44:28 +02:00
target . AllowedActions = source . AllowedActions ;
target . AllowedTemplates = source . AllowedTemplates ;
target . AllowPreview = source . AllowPreview ;
target . ContentApps = source . ContentApps ;
target . ContentDto = source . ContentDto ;
target . ContentTypeAlias = source . ContentTypeAlias ;
target . ContentTypeId = source . ContentTypeId ;
target . ContentTypeKey = source . ContentTypeKey ;
target . ContentTypeName = source . ContentTypeName ;
target . DocumentType = source . DocumentType ;
target . Errors = source . Errors ;
target . Icon = source . Icon ;
target . Id = source . Id ;
target . IsBlueprint = source . IsBlueprint ;
target . IsChildOfListView = source . IsChildOfListView ;
target . IsContainer = source . IsContainer ;
target . IsElement = source . IsElement ;
target . Key = source . Key ;
target . Owner = source . Owner ;
target . ParentId = source . ParentId ;
target . Path = source . Path ;
target . PersistedContent = source . PersistedContent ;
target . SortOrder = source . SortOrder ;
target . TemplateAlias = source . TemplateAlias ;
target . TemplateId = source . TemplateId ;
target . Trashed = source . Trashed ;
target . TreeNodeUrl = source . TreeNodeUrl ;
target . Udi = source . Udi ;
target . UpdateDate = source . UpdateDate ;
target . Updater = source . Updater ;
target . Urls = source . Urls ;
target . Variants = source . Variants ;
2022-06-20 08:37:17 +02:00
}
// Umbraco.Code.MapAll
private static void Map ( IContent source , ContentPropertyCollectionDto target , MapperContext context ) = >
target . Properties = context . MapEnumerable < IProperty , ContentPropertyDto > ( source . Properties ) . WhereNotNull ( ) ;
2018-09-12 12:08:51 +01:00
2022-06-20 08:37:17 +02:00
// Umbraco.Code.MapAll -AllowPreview -Errors -PersistedContent
private void Map < TVariant > ( IContent source , ContentItemDisplay < TVariant > target , MapperContext context )
where TVariant : ContentVariantDisplay
{
// Both GetActions and DetermineIsChildOfListView use parent, so get it once here
// Parent might already be in context, so check there before using content service
IContent ? parent ;
if ( context . Items . TryGetValue ( "Parent" , out var parentObj ) & &
parentObj is IContent typedParent )
2019-03-26 18:47:35 +01:00
{
2022-06-20 08:37:17 +02:00
parent = typedParent ;
2019-03-26 18:47:35 +01:00
}
2022-06-20 08:37:17 +02:00
else
2019-03-26 13:58:38 +01:00
{
2022-06-20 08:37:17 +02:00
parent = _contentService . GetParent ( source ) ;
2019-03-26 13:58:38 +01:00
}
2018-09-12 12:08:51 +01:00
2022-06-20 08:37:17 +02:00
target . AllowedActions = GetActions ( source , parent , context ) ;
target . AllowedTemplates = GetAllowedTemplates ( source ) ;
target . ContentApps = _commonMapper . GetContentAppsForEntity ( source ) ;
target . ContentTypeId = source . ContentType . Id ;
target . ContentTypeKey = source . ContentType . Key ;
target . ContentTypeAlias = source . ContentType . Alias ;
target . ContentTypeName =
_localizedTextService . UmbracoDictionaryTranslate ( _cultureDictionary , source . ContentType . Name ) ;
target . DocumentType = _commonMapper . GetContentType ( source , context ) ;
target . Icon = source . ContentType . Icon ;
target . Id = source . Id ;
target . IsBlueprint = source . Blueprint ;
target . IsChildOfListView = DetermineIsChildOfListView ( source , parent , context ) ;
target . IsContainer = source . ContentType . IsContainer ;
target . IsElement = source . ContentType . IsElement ;
target . Key = source . Key ;
target . Owner = _commonMapper . GetOwner ( source , context ) ;
target . ParentId = source . ParentId ;
target . Path = source . Path ;
target . SortOrder = source . SortOrder ;
target . TemplateAlias = GetDefaultTemplate ( source ) ;
target . TemplateId = source . TemplateId ? ? default ;
target . Trashed = source . Trashed ;
target . TreeNodeUrl = _commonTreeNodeMapper . GetTreeNodeUrl < ContentTreeController > ( source ) ;
target . Udi =
Udi . Create ( source . Blueprint ? Constants . UdiEntityType . DocumentBlueprint : Constants . UdiEntityType . Document , source . Key ) ;
target . UpdateDate = source . UpdateDate ;
target . Updater = _commonMapper . GetCreator ( source , context ) ;
target . Urls = GetUrls ( source ) ;
target . Variants = _contentVariantMapper . Map < TVariant > ( source , context ) ;
target . ContentDto = new ContentPropertyCollectionDto
2019-03-26 13:58:38 +01:00
{
2022-06-20 08:37:17 +02:00
Properties = context . MapEnumerable < IProperty , ContentPropertyDto > ( source . Properties ) . WhereNotNull ( )
} ;
}
2021-06-28 09:32:42 +02:00
2022-06-20 08:37:17 +02:00
// Umbraco.Code.MapAll -Segment -Language -DisplayName
private void Map ( IContent source , ContentVariantDisplay target , MapperContext context )
{
target . CreateDate = source . CreateDate ;
target . Name = source . Name ;
target . PublishDate = source . PublishDate ;
target . State = _stateMapper . Map ( source , context ) ;
target . Tabs = _tabsAndPropertiesMapper . Map ( source , context ) ;
target . UpdateDate = source . UpdateDate ;
2022-07-05 12:40:33 +02:00
target . AllowedActions = new [ ] { ActionBrowse . ActionLetter . ToString ( ) } ;
2022-06-20 08:37:17 +02:00
}
2018-09-13 12:07:33 +01:00
2022-06-20 08:37:17 +02:00
private void Map ( IContent source , ContentVariantScheduleDisplay target , MapperContext context )
{
Map ( source , ( ContentVariantDisplay ) target , context ) ;
target . ReleaseDate = GetScheduledDate ( source , ContentScheduleAction . Release , context ) ;
target . ExpireDate = GetScheduledDate ( source , ContentScheduleAction . Expire , context ) ;
}
// Umbraco.Code.MapAll -Alias
private void Map ( IContent source , ContentItemBasic < ContentPropertyBasic > target , MapperContext context )
{
target . ContentTypeId = source . ContentType . Id ;
target . ContentTypeAlias = source . ContentType . Alias ;
target . CreateDate = source . CreateDate ;
target . Edited = source . Edited ;
target . Icon = source . ContentType . Icon ;
target . Id = source . Id ;
target . Key = source . Key ;
target . Name = GetName ( source , context ) ;
target . Owner = _commonMapper . GetOwner ( source , context ) ;
target . ParentId = source . ParentId ;
target . Path = source . Path ;
target . Properties = context . MapEnumerable < IProperty , ContentPropertyBasic > ( source . Properties ) . WhereNotNull ( ) ;
target . SortOrder = source . SortOrder ;
target . State = _basicStateMapper . Map ( source , context ) ;
target . Trashed = source . Trashed ;
target . Udi =
Udi . Create ( source . Blueprint ? Constants . UdiEntityType . DocumentBlueprint : Constants . UdiEntityType . Document , source . Key ) ;
target . UpdateDate = GetUpdateDate ( source , context ) ;
target . Updater = _commonMapper . GetCreator ( source , context ) ;
target . VariesByCulture = source . ContentType . VariesByCulture ( ) ;
}
private IEnumerable < string > GetActions ( IContent source , IContent ? parent , MapperContext context )
{
2022-07-11 10:37:25 +02:00
context . Items . TryGetValue ( "CurrentUser" , out var currentBackofficeUser ) ;
2022-06-20 08:37:17 +02:00
2022-07-11 10:37:25 +02:00
IUser ? currentUser = null ;
if ( currentBackofficeUser is IUser currentIUserBackofficeUser )
{
currentUser = currentIUserBackofficeUser ;
}
else if ( _backOfficeSecurityAccessor . BackOfficeSecurity ? . CurrentUser is not null )
{
currentUser = _backOfficeSecurityAccessor . BackOfficeSecurity . CurrentUser ;
}
if ( currentUser is null )
2021-10-19 11:29:25 +01:00
{
2022-06-20 08:37:17 +02:00
return Enumerable . Empty < string > ( ) ;
2021-10-19 11:29:25 +01:00
}
2022-06-20 08:37:17 +02:00
string path ;
if ( source . HasIdentity )
2019-03-26 13:58:38 +01:00
{
2022-06-20 08:37:17 +02:00
path = source . Path ;
2018-09-11 12:33:32 +01:00
}
2022-06-20 08:37:17 +02:00
else
2018-09-11 12:33:32 +01:00
{
2022-06-20 08:37:17 +02:00
path = parent = = null ? "-1" : parent . Path ;
2018-09-11 12:33:32 +01:00
}
2019-03-26 13:58:38 +01:00
2022-06-20 08:37:17 +02:00
// A bit of a mess, but we need to ensure that all the required values are here AND that they're the right type.
2022-07-11 10:37:25 +02:00
if ( context . Items . TryGetValue ( "Permissions" , out var permissionsObject ) & & permissionsObject is Dictionary < string , EntityPermissionSet > permissionsDict )
2018-09-11 12:33:32 +01:00
{
2022-06-20 08:37:17 +02:00
// If we already have permissions for a given path,
// and the current user is the same as was used to generate the permissions, return the stored permissions.
2022-07-11 10:37:25 +02:00
if ( _backOfficeSecurityAccessor . BackOfficeSecurity ? . CurrentUser ? . Id = = currentUser . Id & &
2022-06-20 08:37:17 +02:00
permissionsDict . TryGetValue ( path , out EntityPermissionSet ? permissions ) )
2018-09-14 10:08:09 +10:00
{
2022-06-20 08:37:17 +02:00
return permissions . GetAllPermissions ( ) ;
2019-03-26 13:58:38 +01:00
}
2022-06-20 08:37:17 +02:00
}
2018-09-17 13:19:24 +02:00
2022-06-20 08:37:17 +02:00
// TODO: This is certainly not ideal usage here - perhaps the best way to deal with this in the future is
// with the IUmbracoContextAccessor. In the meantime, if used outside of a web app this will throw a null
// reference exception :(
2021-06-28 09:32:42 +02:00
2022-07-11 10:37:25 +02:00
return _userService . GetPermissionsForPath ( currentUser , path ) . GetAllPermissions ( ) ;
2022-06-20 08:37:17 +02:00
}
2021-07-05 20:58:04 +02:00
2022-06-20 08:37:17 +02:00
private UrlInfo [ ] GetUrls ( IContent source )
{
if ( source . ContentType . IsElement )
{
return Array . Empty < UrlInfo > ( ) ;
2019-03-26 13:58:38 +01:00
}
2018-09-13 12:07:33 +01:00
2022-06-20 08:37:17 +02:00
if ( ! _umbracoContextAccessor . TryGetUmbracoContext ( out IUmbracoContext ? umbracoContext ) )
2019-03-26 13:58:38 +01:00
{
2022-06-20 08:37:17 +02:00
return new [ ] { UrlInfo . Message ( "Cannot generate URLs without a current Umbraco Context" ) } ;
}
2021-01-08 00:25:09 +11:00
2022-06-20 08:37:17 +02:00
// NOTE: unfortunately we're not async, we'll use .Result and hope this won't cause a deadlock anywhere for now
UrlInfo [ ] urls = source . GetContentUrlsAsync (
_publishedRouter ,
umbracoContext ,
_localizationService ,
_localizedTextService ,
_contentService ,
_variationContextAccessor ,
_loggerFactory . CreateLogger < IContent > ( ) ,
_uriUtility ,
_publishedUrlProvider )
. ConfigureAwait ( false )
. GetAwaiter ( )
. GetResult ( )
. ToArray ( ) ;
return urls ;
}
2019-03-26 13:58:38 +01:00
2022-06-20 08:37:17 +02:00
private DateTime GetUpdateDate ( IContent source , MapperContext context )
{
// invariant = global date
if ( ! source . ContentType . VariesByCulture ( ) )
{
return source . UpdateDate ;
2018-09-11 12:33:32 +01:00
}
2019-01-03 09:27:52 +01:00
2022-06-20 08:37:17 +02:00
// variant = depends on culture
var culture = context . GetCulture ( ) ;
2019-03-26 13:58:38 +01:00
2022-06-20 08:37:17 +02:00
// if there's no culture here, the issue is somewhere else (UI, whatever) - throw!
if ( culture = = null )
{
throw new InvalidOperationException ( "Missing culture in mapping options." ) ;
}
2019-03-26 13:58:38 +01:00
2022-06-20 08:37:17 +02:00
// if we don't have a date for a culture, it means the culture is not available, and
// hey we should probably not be mapping it, but it's too late, return a fallback date
DateTime ? date = source . GetUpdateDate ( culture ) ;
return date ? ? source . UpdateDate ;
}
2019-03-26 13:58:38 +01:00
2022-06-20 08:37:17 +02:00
private string? GetName ( IContent source , MapperContext context )
{
// invariant = only 1 name
if ( ! source . ContentType . VariesByCulture ( ) )
{
return source . Name ;
2019-03-26 13:58:38 +01:00
}
2019-01-03 09:27:52 +01:00
2022-06-20 08:37:17 +02:00
// variant = depends on culture
var culture = context . GetCulture ( ) ;
2019-01-03 09:27:52 +01:00
2022-06-20 08:37:17 +02:00
// if there's no culture here, the issue is somewhere else (UI, whatever) - throw!
if ( culture = = null )
{
throw new InvalidOperationException ( "Missing culture in mapping options." ) ;
}
2019-01-03 09:27:52 +01:00
2022-06-20 08:37:17 +02:00
// if we don't have a name for a culture, it means the culture is not available, and
// hey we should probably not be mapping it, but it's too late, return a fallback name
return source . CultureInfos is not null & &
source . CultureInfos . TryGetValue ( culture , out ContentCultureInfos name ) & & ! name . Name . IsNullOrWhiteSpace ( )
? name . Name
: $"({source.Name})" ;
}
2019-03-26 13:58:38 +01:00
2022-06-20 08:37:17 +02:00
/// <summary>
/// Checks if the content item is a descendant of a list view
/// </summary>
/// <param name="source"></param>
/// <param name="parent"></param>
/// <param name="context"></param>
/// <returns>
/// Returns true if the content item is a descendant of a list view and where the content is
/// not a current user's start node.
/// </returns>
/// <remarks>
/// We must check if it's the current user's start node because in that case we will actually be
/// rendering the tree node underneath the list view to visually show context. In this case we return
/// false because the item is technically not being rendered as part of a list view but instead as a
/// real tree node. If we didn't perform this check then tree syncing wouldn't work correctly.
/// </remarks>
private bool DetermineIsChildOfListView ( IContent source , IContent ? parent , MapperContext context )
{
var userStartNodes = Array . Empty < int > ( ) ;
2019-03-26 13:58:38 +01:00
2022-06-20 08:37:17 +02:00
// In cases where a user's start node is below a list view, we will actually render
// out the tree to that start node and in that case for that start node, we want to return
// false here.
if ( context . HasItems & & context . Items . TryGetValue ( "CurrentUser" , out var usr ) & & usr is IUser currentUser )
2019-03-26 13:58:38 +01:00
{
2022-06-20 08:37:17 +02:00
userStartNodes = currentUser . CalculateContentStartNodeIds ( _entityService , _appCaches ) ;
if ( ! userStartNodes ? . Contains ( Constants . System . Root ) ? ? false )
2020-02-12 13:47:48 +11:00
{
2022-06-20 08:37:17 +02:00
// return false if this is the user's actual start node, the node will be rendered in the tree
// regardless of if it's a list view or not
if ( userStartNodes ? . Contains ( source . Id ) ? ? false )
2020-02-19 13:43:48 +11:00
{
2022-06-20 08:37:17 +02:00
return false ;
2020-02-19 13:43:48 +11:00
}
2020-02-12 13:47:48 +11:00
}
2022-06-20 08:37:17 +02:00
}
2020-02-12 13:47:48 +11:00
2022-06-20 08:37:17 +02:00
if ( parent = = null )
{
return false ;
}
2020-02-19 13:43:48 +11:00
2022-06-20 08:37:17 +02:00
var pathParts = parent . Path . Split ( Constants . CharArrays . Comma ) . Select ( x = >
int . TryParse ( x , NumberStyles . Integer , CultureInfo . InvariantCulture , out var i ) ? i : 0 ) . ToList ( ) ;
2020-02-19 13:43:48 +11:00
2022-06-20 08:37:17 +02:00
if ( userStartNodes is not null )
{
// reduce the path parts so we exclude top level content items that
// are higher up than a user's start nodes
foreach ( var n in userStartNodes )
2020-02-19 13:43:48 +11:00
{
2022-06-20 08:37:17 +02:00
var index = pathParts . IndexOf ( n ) ;
if ( index ! = - 1 )
2020-02-19 13:43:48 +11:00
{
2022-06-20 08:37:17 +02:00
// now trim all top level start nodes to the found index
for ( var i = 0 ; i < index ; i + + )
2020-02-19 13:43:48 +11:00
{
2022-06-20 08:37:17 +02:00
pathParts . RemoveAt ( 0 ) ;
2020-02-19 13:43:48 +11:00
}
}
}
2019-03-26 13:58:38 +01:00
}
2022-06-20 08:37:17 +02:00
return parent . ContentType . IsContainer | | _contentTypeService . HasContainerInPath ( pathParts . ToArray ( ) ) ;
}
2020-02-24 15:11:58 +00:00
2022-03-31 14:35:23 +02:00
2022-06-20 08:37:17 +02:00
private DateTime ? GetScheduledDate ( IContent source , ContentScheduleAction action , MapperContext context )
{
_ = context . Items . TryGetValue ( "Schedule" , out var untypedSchedule ) ;
2021-10-19 11:29:25 +01:00
2022-06-20 08:37:17 +02:00
if ( untypedSchedule is not ContentScheduleCollection scheduleCollection )
{
throw new ApplicationException (
"GetScheduledDate requires a ContentScheduleCollection in the MapperContext for Key: Schedule" ) ;
2019-03-26 13:58:38 +01:00
}
2022-06-20 08:37:17 +02:00
var culture = context . GetCulture ( ) ? ? string . Empty ;
IEnumerable < ContentSchedule > schedule = scheduleCollection . GetSchedule ( culture , action ) ;
return schedule . FirstOrDefault ( ) ? . Date ; // take the first, it's ordered by date
}
private IDictionary < string , string? > ? GetAllowedTemplates ( IContent source )
{
// Element types can't have templates, so no need to query to get the content type
if ( source . ContentType . IsElement )
2019-03-26 13:58:38 +01:00
{
2022-06-20 08:37:17 +02:00
return new Dictionary < string , string? > ( ) ;
}
2021-06-28 09:32:42 +02:00
2022-06-20 08:37:17 +02:00
IContentType ? contentType = _contentTypeService . Get ( source . ContentTypeId ) ;
2019-03-26 13:58:38 +01:00
2022-06-20 08:37:17 +02:00
return contentType ? . AllowedTemplates ?
. Where ( t = > t . Alias . IsNullOrWhiteSpace ( ) = = false & & t . Name . IsNullOrWhiteSpace ( ) = = false )
. ToDictionary ( t = > t . Alias , t = > _localizedTextService . UmbracoDictionaryTranslate ( _cultureDictionary , t . Name ) ) ;
}
2019-01-03 09:27:52 +01:00
2022-06-20 08:37:17 +02:00
private string? GetDefaultTemplate ( IContent source )
{
if ( source = = null )
2019-03-26 13:58:38 +01:00
{
2022-06-20 08:37:17 +02:00
return null ;
}
2019-03-26 13:58:38 +01:00
2022-06-20 08:37:17 +02:00
// If no template id was set...
if ( ! source . TemplateId . HasValue )
{
// ... and no default template is set, return null...
// ... otherwise return the content type default template alias.
return string . IsNullOrWhiteSpace ( source . ContentType . DefaultTemplate ? . Alias )
? null
: source . ContentType . DefaultTemplate ? . Alias ;
2019-01-03 09:27:52 +01:00
}
2022-06-20 08:37:17 +02:00
ITemplate ? template = _fileService . GetTemplate ( source . TemplateId . Value ) ;
return template ? . Alias ;
2019-01-03 09:27:52 +01:00
}
2017-07-20 11:21:28 +02:00
}