public IEnumerable <ViewDto> ViewUsage(int appId, Guid guid, Func <List <IView>, List <BlockConfiguration>, IEnumerable <ViewDto> > finalBuilder) { var wrapLog = Log.Call <IEnumerable <ViewDto> >($"{appId}, {guid}"); var context = _ctxResolver.BlockOrApp(appId); // extra security to only allow zone change if host user var permCheck = ServiceProvider.Build <MultiPermissionsApp>().Init(context, context.AppState, Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } var cms = _cmsRuntime.Init(context.AppState, context.UserMayEdit, Log); // treat view as a list - in case future code will want to analyze many views together var views = new List <IView> { cms.Views.Get(guid) }; var blocks = cms.Blocks.AllWithView(); Log.Add($"Found {blocks.Count} content blocks"); var result = finalBuilder(views, blocks); return(wrapLog("ok", result)); }
public DialogContextStandalone DialogSettings(int appId) { // reset app-id if we get a info-token like -100 if (appId < 0) { appId = Eav.Constants.AppIdEmpty; } var appContext = appId != Eav.Constants.AppIdEmpty ? _ctxResolver.App(appId) : null; var context = appContext ?? _ctxResolver.Site(); // if we have an appid (we don't have it in an install-new-apps-scenario) check permissions if (appContext != null) { var appAndPerms = ServiceProvider.Build <MultiPermissionsApp>().Init(appContext, appContext.AppState, Log); if (!appAndPerms.ZoneIsOfCurrentContextOrUserIsSuper(out var error)) { throw HttpException.PermissionDenied(error); } } var cb = _uiContextBuilder.SetZoneAndApp(context.Site.ZoneId, appContext?.AppState); return(new DialogContextStandalone { Context = cb.Get(Ctx.All), }); }
public Sxc.Adam.IFile SaveInAdam(string dontRelyOnParameterOrder = Eav.Constants.RandomProtectionParameter, Stream stream = null, string fileName = null, string contentType = null, Guid?guid = null, string field = null, string subFolder = "") { Eav.Constants.ProtectAgainstMissingParameterNames(dontRelyOnParameterOrder, "SaveInAdam", $"{nameof(stream)},{nameof(fileName)},{nameof(contentType)},{nameof(guid)},{nameof(field)},{nameof(subFolder)} (optional)"); if (stream == null || fileName == null || contentType == null || guid == null || field == null) { throw new Exception(); } var feats = new[] { FeatureIds.UseAdamInWebApi, FeatureIds.PublicUpload }; if (!Eav.Configuration.Features.EnabledOrException(feats, "can't save in ADAM", out var exp)) { throw exp; } var appId = DynCode?.Block?.AppId ?? DynCode?.App?.AppId ?? throw new Exception("Error, SaveInAdam needs an App-Context to work, but the App is not known."); return(ServiceProvider.Build <AdamTransUpload <int, int> >(typeof(AdamTransUpload <int, int>)) .Init(appId, contentType, guid.Value, field, false, Log) .UploadOne(stream, fileName, subFolder, true)); }
public override void OnActionExecuting(ActionExecutingContext context) { base.OnActionExecuting(context); var httpContext = context.HttpContext; ServiceProvider = httpContext.RequestServices; HttpRequest GetRequest() => httpContext.Request; _oqtState = new OqtState(GetRequest, ServiceProvider, Log); var getBlock = _oqtState.GetBlock(true); DynCode = ServiceProvider.Build <OqtaneDynamicCode>().Init(getBlock, Log); var stxResolver = ServiceProvider.Build <IContextResolver>(typeof(IContextResolver)); stxResolver.AttachRealBlock(() => getBlock); stxResolver.AttachBlockContext(() => _oqtState.GetContext()); if (context.HttpContext.Items.TryGetValue(CodeCompiler.SharedCodeRootPathKeyInCache, out var createInstancePath)) { CreateInstancePath = createInstancePath as string; } }
internal void RemoveAppInSiteAndEav(int appId, bool fullDelete) { var zoneId = ZoneRuntime.ZoneId; // check portal assignment and that it's not the default app if (appId == ZoneRuntime.DefaultAppId) { throw new Exception("The default app of a zone cannot be removed."); } // todo: maybe verify the app is of this portal; I assume delete will fail anyhow otherwise // Prepare to Delete folder in dnn - this must be done, before deleting the app in the DB var sexyApp = ServiceProvider.Build <App>().InitNoData(new AppIdentity(zoneId, appId), null); var folder = sexyApp.Folder; var physPath = sexyApp.PhysicalPath; // now remove from DB. This sometimes fails, so we do this before trying to clean the files // as the db part should be in a transaction, and if it fails, everything should stay as is _zoneManagerLazy.Value.Init(zoneId, Log).DeleteApp(appId, fullDelete); // now really delete the files - if the DB didn't end up throwing an error // ...but only if it's a full-delete if (fullDelete && !string.IsNullOrEmpty(folder) && Directory.Exists(physPath)) { Directory.Delete(physPath, true); } }
protected void ThrowIfNotAllowedInApp(List <Grants> requiredGrants, IAppIdentity alternateApp = null) { var permCheck = ServiceProvider.Build <MultiPermissionsApp>().Init(ContextOfBlock, alternateApp ?? ContextOfBlock.AppState, Log); if (!permCheck.EnsureAll(requiredGrants, out var error)) { throw HttpException.PermissionDenied(error); } }
private IEnumerable <AdamItemDto> GetAdamListOfItems(int appId, BundleWithLinkField set, string key) { var adamListMaker = ServiceProvider.Build <IAdamTransGetItems>(); adamListMaker.Init(appId, set.ContentTypeName, set.Guid, key, false, Log); var dic = adamListMaker.ItemsInField(string.Empty, false) as IEnumerable <AdamItemDto>; return(dic); }
public string NewBlockAndRender(int parentId, string field, int sortOrder, string app = "", Guid?guid = null) { var entityId = NewBlock(parentId, field, sortOrder, app, guid); // now return a rendered instance var newContentBlock = ServiceProvider.Build <BlockFromEntity>().Init(Block, entityId, Log); return(newContentBlock.BlockBuilder.Render()); }
public IEnumerable <AppUiInfo> Apps(string apps = null) { // Note: we must get the zone-id from the tenant, since the app may not yet exist when inserted the first time var tenant = ContextOfBlock.Site; return(ServiceProvider.Build <CmsZones>().Init(tenant.ZoneId, Log) .AppsRt .GetSelectableApps(tenant, apps) .ToList()); }
protected MultiPermissionsItems ThrowIfNotAllowedInItem(IEntity itm, List <Grants> requiredGrants, IAppIdentity alternateApp = null) { var permCheck = ServiceProvider.Build <MultiPermissionsItems>().Init(Context, alternateApp ?? Context.AppState, itm, Log); if (!permCheck.EnsureAll(requiredGrants, out var error)) { throw HttpException.PermissionDenied(error); } return(permCheck); }
/// <summary> /// Returns all Apps for the current zone /// </summary> /// <returns></returns> public List <IApp> GetApps(ISite site, Func <Eav.Apps.App, IAppDataConfiguration> buildConfig) { var zId = ZoneRuntime.ZoneId; var appIds = new ZoneRuntime().Init(zId, Log).Apps; return(appIds .Select(a => ServiceProvider.Build <App>() .PreInit(site) .Init(new AppIdentity(zId, a.Key), buildConfig, Log) as IApp) .OrderBy(a => a.Name) .ToList()); }
public string ResolveHyperlink(int appId, string hyperlink, string contentType, Guid guid, string field) { try { var context = _ctxResolver.App(appId); // different security checks depending on the link-type var lookupPage = hyperlink.Trim().StartsWith("page", StringComparison.OrdinalIgnoreCase); // look it up first, because we need to know if the result is in ADAM or not (different security scenario) var conv = ServiceProvider.Build <IValueConverter>(); var resolved = conv.ToValue(hyperlink, guid); if (lookupPage) { // page link - only resolve if the user has edit-permissions // only people who have some full edit permissions may actually look up pages var permCheckPage = ServiceProvider.Build <MultiPermissionsApp>().Init(context, context.AppState, Log); return(permCheckPage.UserMayOnAll(GrantSets.WritePublished) ? resolved : hyperlink); } // for file, we need guid & field - otherwise return the original unmodified if (guid == default || string.IsNullOrEmpty(field) || string.IsNullOrEmpty(contentType)) { return(hyperlink); } var isOutsideOfAdam = !(resolved.IndexOf($"/{AdamConstants.AdamRootFolder}/", StringComparison.Ordinal) > 0); // file-check, more abilities to allow // this will already do a ensure-or-throw inside it if outside of adam var adamCheck = AdamState; // new AdamState<int, int>(); adamCheck.Init(context, contentType, field, guid, isOutsideOfAdam, Log); if (!adamCheck.Security.SuperUserOrAccessingItemFolder(resolved, out var exp)) { throw exp; } if (!adamCheck.Security.UserIsPermittedOnField(GrantSets.ReadSomething, out exp)) { throw exp; } // if everything worked till now, it's ok to return the result return(resolved); } catch { return(hyperlink); } }
public Dictionary <string, IEnumerable <Dictionary <string, object> > > Query(int?appId, string name, bool includeGuid, string stream) { var wrapLog = Log.Call($"'{name}', inclGuid: {includeGuid}, stream: {stream}"); var appCtx = appId != null?_ctxResolver.BlockOrApp(appId.Value) : _ctxResolver.BlockRequired(); // If no app available from context, check if an app-id was supplied in url // Note that it may only be an app from the current portal // and security checks will run internally var app = ServiceProvider.Build <Apps.App>().Init(ServiceProvider, appCtx.AppState.AppId, Log, appCtx.UserMayEdit); var result = BuildQueryAndRun(app, name, stream, includeGuid, appCtx, Log, appCtx.UserMayEdit); wrapLog(null); return(result); }
private LinkInfoDto TryToConvertOrErrorText(int appId, string contentType, string field, string value, Guid entityGuid) { try { var hlnkBackend = ServiceProvider.Build <HyperlinkBackend <int, int> >().Init(Log); var result = hlnkBackend.LookupHyperlink(appId, value, contentType, entityGuid, field); return(result);// _valueConverter.ToValue(value, entityGuid); } catch { return(new LinkInfoDto { Value = "error" }); //return "error"; } }
// New feature in 11.03 - Usage Statistics public dynamic Usage(int appId, Guid guid) { var context = _ctxResolver.App(appId); var permCheck = ServiceProvider.Build <MultiPermissionsApp>().Init(context, context.AppState, Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } var item = context.AppState.List.One(guid); var relationships = item.Relationships.AllRelationships; // var result = relationships.Select(r => new EntityInRelationDto(r.)) // todo: don't forget Metadata relationships return(null); }
public IEnumerable <EntityForPickerDto> GetAvailableEntities(int appId, string[] items, string contentTypeName) { var context = _ctxResolver.App(appId); // do security check var permCheck = string.IsNullOrEmpty(contentTypeName) ? ServiceProvider.Build <MultiPermissionsApp>().Init(context, context.AppState, Log) : ServiceProvider.Build <MultiPermissionsTypes>().Init(context, context.AppState, contentTypeName, Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } // maybe in the future, ATM not relevant var withDrafts = permCheck.EnsureAny(GrantSets.ReadDraft); return(_entityPickerApi.Init(Log).GetAvailableEntities(appId, items, contentTypeName, withDrafts)); }
public Dictionary <string, IEnumerable <Dictionary <string, object> > > PublicQuery(string appPath, string name, string stream) { var wrapLog = Log.Call($"path:{appPath}, name:{name}"); if (string.IsNullOrEmpty(name)) { throw HttpException.MissingParam(nameof(name)); } var appCtx = _ctxResolver.AppOrBlock(appPath); var queryApp = ServiceProvider.Build <Apps.App>().Init(appCtx.AppState, ServiceProvider.Build <AppConfigDelegate>().Init(Log).Build(appCtx.UserMayEdit), Log); // now just run the default query check and serializer var result = BuildQueryAndRun(queryApp, name, stream, false, appCtx, Log, appCtx.UserMayEdit); wrapLog(null); return(result); }
public List <AppDto> Apps() { var cms = _cmsZones.Init(_context.Site.ZoneId, Log); var configurationBuilder = ServiceProvider.Build <AppConfigDelegate>().Init(Log).Build(_context.UserMayEdit); var list = cms.AppsRt.GetApps(_context.Site, configurationBuilder); return(list.Select(a => new AppDto { Id = a.AppId, IsApp = a.AppGuid != Eav.Constants.DefaultAppName, Guid = a.AppGuid, Name = a.Name, Folder = a.Folder, AppRoot = a.Path, IsHidden = a.Hidden, ConfigurationId = a.Configuration?.Id, Items = a.Data.List.Count(), Thumbnail = a.Thumbnail, Version = a.VersionSafe() }).ToList()); }
private Dictionary <string, IEnumerable <AdamItemDto> > PrefetchAdam(int appId, EditDto editData) { // Step 1: try to find hyperlink fields var bundlesHavingLinks = BundleWithLinkFields(editData); var links = bundlesHavingLinks.SelectMany(set => set.Hyperlink.Select(h => { var adamListMaker = ServiceProvider.Build <IAdamTransGetItems>(); adamListMaker.Init(appId, set.ContentTypeName, set.Guid, h.Key, false, Log); return(new { set.Guid, h.Key, Dic = adamListMaker.ItemsInField(string.Empty) as IEnumerable <AdamItemDto>, }); })) //.Where(set => set != null) // Step 2: Check which ones have a link reference .ToDictionary(r => r.Key, r => r.Dic); return(links); }
public Dictionary <Guid, int> Save(EditDto package, bool partOfPage) { Log.Add($"save started with a#{_appId}, i⋮{package.Items.Count}, partOfPage:{partOfPage}"); var validator = new SaveDataValidator(package, Log); // perform some basic validation checks if (!validator.ContainsOnlyExpectedNodes(out var exp)) { throw exp; } // todo: unsure about this - thought I should check contentblockappid in group-header, because this is where it should be saved! //var contextAppId = appId; //var targetAppId = package.Items.First().Header.Group.ContentBlockAppId; //if (targetAppId != 0) //{ // Log.Add($"detected content-block app to use: {targetAppId}; in context of app {contextAppId}"); // appId = targetAppId; //} var appMan = _appManagerLazy.Value.Init(_appId, Log); var appRead = appMan.Read; var ser = ServiceProvider.Build <JsonSerializer>().Init(appRead.AppState, Log); // Since we're importing directly into this app, we would prefer local content-types ser.PreferLocalAppTypes = true; validator.PrepareForEntityChecks(appRead); #region check if it's an update, and do more security checks then - shared with EntitiesController.Save // basic permission checks var permCheck = new Save.SaveSecurity(_context, Log) .DoPreSaveSecurityCheck(_appId, package.Items); var foundItems = package.Items.Where(i => i.Entity.Id != 0 && i.Entity.Guid != Guid.Empty) .Select(i => i.Entity.Guid != Guid.Empty ? appRead.Entities.Get(i.Entity.Guid) // prefer guid access if available : appRead.Entities.Get(i.Entity.Id) // otherwise id ); if (foundItems.Any(i => i != null) && !permCheck.EnsureAll(GrantSets.UpdateSomething, out var error)) { throw HttpException.PermissionDenied(error); } #endregion var items = package.Items.Select(i => { var ent = ser.Deserialize(i.Entity, false, false) as Entity; var index = package.Items.IndexOf(i); // index is helpful in case of errors if (!validator.EntityIsOk(index, ent, out exp)) { throw exp; } if (!validator.IfUpdateValidateAndCorrectIds(index, ent, out exp)) { throw exp; } ent.IsPublished = package.IsPublished; ent.PlaceDraftInBranch = package.DraftShouldBranch; // new in 11.01 if (i.Header.ListHas()) { // Check if Add was true, and fix if it had already been saved (EntityId != 0) // the entityId is reset by the validator if it turns out to be an update // todo: verify use - maybe it's to set before we save, as maybe afterwards it's always != 0? var add = i.Header.ListAdd(); i.Header.Add = add; if (ent.EntityId > 0 && add) { i.Header.Add = false; } } return(new BundleWithHeader <IEntity> { Header = i.Header, Entity = ent }); }) .ToList(); Log.Add("items to save generated, all data tests passed"); return(_pagePublishing.SaveInPagePublishing(_ctxResolver.RealBlockOrNull(), _appId, items, partOfPage, forceSaveAsDraft => DoSave(appMan, items, forceSaveAsDraft), permCheck)); }
// 2021-04-13 2dm should be unused now //public string ResolveHyperlink(int appId, string hyperlink, string contentType, Guid guid, string field) //{ // try // { // var context = _ctxResolver.BlockOrApp(appId); // // different security checks depending on the link-type // var lookupPage = hyperlink.Trim().StartsWith(ValueConverterBase.PrefixPage, StringComparison.OrdinalIgnoreCase); // // look it up first, because we need to know if the result is in ADAM or not (different security scenario) // var conv = ServiceProvider.Build<IValueConverter>(); // var resolved = conv.ToValue(hyperlink, guid); // if (lookupPage) // { // // page link - only resolve if the user has edit-permissions // // only people who have some full edit permissions may actually look up pages // var permCheckPage = ServiceProvider.Build<MultiPermissionsApp>().Init(context, context.AppState, Log); // return permCheckPage.UserMayOnAll(GrantSets.WritePublished) // ? resolved // : hyperlink; // } // // for file, we need guid & field - otherwise return the original unmodified // if (guid == default || string.IsNullOrEmpty(field) || string.IsNullOrEmpty(contentType)) // return hyperlink; // var isOutsideOfAdam = !(resolved.IndexOf($"/{AdamConstants.AdamRootFolder}/", StringComparison.Ordinal) > 0); // // file-check, more abilities to allow // // this will already do a ensure-or-throw inside it if outside of adam // var adamCheck = AdamContext; // new AdamState<int, int>(); // adamCheck.Init(context, contentType, field, guid, isOutsideOfAdam, Log); // if (!adamCheck.Security.SuperUserOrAccessingItemFolder(resolved, out var exp)) // throw exp; // if (!adamCheck.Security.UserIsPermittedOnField(GrantSets.ReadSomething, out exp)) // throw exp; // // if everything worked till now, it's ok to return the result // return resolved; // } // catch // { // return hyperlink; // } //} public LinkInfoDto LookupHyperlink(int appId, string hyperlink, string contentType, Guid guid, string field) { try { var context = _ctxResolver.BlockOrApp(appId); // different security checks depending on the link-type var lookupPage = hyperlink.Trim().StartsWith(ValueConverterBase.PrefixPage, StringComparison.OrdinalIgnoreCase); // look it up first, because we need to know if the result is in ADAM or not (different security scenario) var conv = ServiceProvider.Build <IValueConverter>(); var resolved = conv.ToValue(hyperlink, guid); if (lookupPage) { // page link - only resolve if the user has edit-permissions // only people who have some full edit permissions may actually look up pages var permCheckPage = ServiceProvider.Build <MultiPermissionsApp>().Init(context, context.AppState, Log); var userMay = permCheckPage.UserMayOnAll(GrantSets.WritePublished); return(new LinkInfoDto { Value = userMay ? resolved : hyperlink }); } // for file, we need guid & field - otherwise return the original unmodified if (guid == default || string.IsNullOrEmpty(field) || string.IsNullOrEmpty(contentType)) { return new LinkInfoDto { Value = hyperlink } } ; var isOutsideOfAdam = !Sxc.Adam.Security.PathIsInItemAdam(guid, field, resolved); // file-check, more abilities to allow // this will already do a ensure-or-throw inside it if outside of adam var adamContext = AdamContext; adamContext.Init(context, contentType, field, guid, isOutsideOfAdam, Log); if (!adamContext.Security.SuperUserOrAccessingItemFolder(resolved, out var exp)) { throw exp; } if (!adamContext.Security.UserIsPermittedOnField(GrantSets.ReadSomething, out exp)) { throw exp; } // now try to find the item // we already know that the link was able to match, so we'll just use this to get the id var parts = new ValueConverterBase.LinkParts(hyperlink); // Note: kind of temporary solution, will fail if TFileId isn't int! var file = ((IAdamFileSystem <int, int>)adamContext.AdamManager.AdamFs).GetFile(parts.Id); var dtoMaker = AdamContext.ServiceProvider.Build <AdamItemDtoMaker <TFolderId, TFileId> >().Init(AdamContext); // if everything worked till now, it's ok to return the result var adam = dtoMaker.Create(file as File <TFolderId, TFileId>); return(new LinkInfoDto { Adam = adam, Value = adam.Url }); } catch { return(new LinkInfoDto { Value = hyperlink }); } }
public EditDto Load(int appId, List <ItemIdentifier> items) { // Security check var wrapLog = Log.Call($"load many a#{appId}, items⋮{items.Count}"); var context = _ctxResolver.App(appId); var showDrafts = context.UserMayEdit; // do early permission check - but at this time it may be that we don't have the types yet // because they may be group/id combinations, without type information which we'll look up afterwards var appIdentity = State.Identity(null, appId); items = _contentGroupList.Init(appIdentity, Log, showDrafts).ConvertListIndexToId(items); TryToAutoFindMetadataSingleton(items, context.AppState); // now look up the types, and repeat security check with type-names // todo: 2020-03-20 new feat 11.01, may not check inner type permissions ATM var permCheck = ServiceProvider.Build <MultiPermissionsTypes>().Init(context, context.AppState, items, Log); if (!permCheck.EnsureAll(GrantSets.WriteSomething, out var error)) { throw HttpException.PermissionDenied(error); } // load items - similar var result = new EditDto(); var entityApi = _entityApi.Init(appId, permCheck.EnsureAny(GrantSets.ReadDraft), Log); var typeRead = entityApi.AppRead.ContentTypes; var list = entityApi.GetEntitiesForEditing(items); var jsonSerializer = ServiceProvider.Build <JsonSerializer>(); result.Items = list.Select(e => new BundleWithHeader <JsonEntity> { Header = e.Header, Entity = GetSerializeAndMdAssignJsonEntity(appId, e, jsonSerializer, typeRead) }).ToList(); // set published if some data already exists if (list.Any()) { result.IsPublished = list.First().Entity?.IsPublished ?? true; // Entity could be null (new), then true // only set draft-should-branch if this draft already has a published item if (!result.IsPublished) { result.DraftShouldBranch = list.First().Entity?.GetPublished() != null; } } // since we're retrieving data - make sure we're allowed to // this is to ensure that if public forms only have create permissions, they can't access existing data // important, this code is shared/duplicated in the EntitiesController.GetManyForEditing if (list.Any(set => set.Entity != null)) { if (!permCheck.EnsureAll(GrantSets.ReadSomething, out error)) { throw HttpException.PermissionDenied(error); } } // load content-types var types = UsedTypes(list, typeRead); result.ContentTypes = types .Select(ct => JsonSerializer.ToJson(ct, true)) .ToList(); //// TEMP DEBUG //var inputTypes = types.SelectMany(t => t.Attributes.Select(a => //{ // var Lookup = a.InputType(); // var mdAll = a.Metadata.Where(md => md.Type.Is(Constants.MetadataFieldTypeAll)).ToList(); // var MdAllCount = mdAll.Count; // var MdAllAttribCount = mdAll.FirstOrDefault()?.Attributes.Count; // var MdAllWithAttrib = mdAll.FirstOrDefault(md => md.Attributes.ContainsKey(Constants.MetadataFieldTypeAll)); // var MdAllAttrib = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll); // var MdAllAttribZero = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll, new string[0]); // var MdAllAttribEmpty = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll, new []{""}); // var MdAllAttribEn = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll, new[] { "en-us" }); // var MdAllAttribTr = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll, new[] { "tr-tr" }); // var MdAllType = a.Metadata.GetBestValue<string>(Constants.MetadataFieldAllInputType, Constants.MetadataFieldTypeAll); // return new {Lookup, MdAllCount, MdAllType, MdAllAttribCount, MdAllWithAttrib?.EntityId, MdAllAttrib, MdAllAttribZero, MdAllAttribEmpty, MdAllAttribEn, MdAllAttribTr }; //})); //var serializedDebug = JsonConvert.SerializeObject(inputTypes); //Log.Add("Test / debug: " + serializedDebug); // ensure that sub-properties of the content-types are included // this is for UI Formulas (children of @All) - WIP // and the warning/error Regex specials - WIP var entList = types.SelectMany( // in all Content-Type attributes like title, body etc. t => t.Attributes.SelectMany( // check all metadata of these attributes - get possible sub-entities attached a => a.Metadata.SelectMany(m => m.Children()) ) ); result.ContentTypeItems = entList.Select(e => jsonSerializer.ToJson(e, 0, Log)).ToList(); // Fix not-supported input-type names; map to correct name result.ContentTypes .ForEach(jt => jt.Attributes .ForEach(at => at.InputType = InputTypes.MapInputTypeV10(at.InputType))); // load input-field configurations result.InputTypes = GetNecessaryInputTypes(result.ContentTypes, typeRead); // also include UI features result.Features = FeaturesHelpers.FeatureListWithPermissionCheck(permCheck).ToList(); // Attach context, but only the minimum needed for the UI result.Context = _contextBuilder.SetZoneAndApp(appIdentity.ZoneId, context.AppState) .Get(Ctx.AppBasic | Ctx.Language | Ctx.Site | Ctx.System); try { result.Prefetch = TryToPrefectAdditionalData(appId, result); } catch (Exception) { /* ignore */ } // done - return wrapLog($"ready, sending items:{result.Items.Count}, " + $"types:{result.ContentTypes.Count}, " + $"inputs:{result.InputTypes.Count}, " + $"feats:{result.Features.Count}"); return(result); }