Example #1
0
        public AllInOneDto Load(IBlock block, IContextBuilder contextBuilder, int appId, List <ItemIdentifier> items)
        {
            // Security check
            var wraplog = Log.Call($"load many a#{appId}, items⋮{items.Count}");

            // 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);

            //var block = GetBlock();
            items = new ContentGroupList(block, Log).ConvertListIndexToId(items, appIdentity);

            // 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 = new MultiPermissionsTypes().Init(block.Context, GetApp(appId, block), items, Log);

            if (!permCheck.EnsureAll(GrantSets.WriteSomething, out var error))
            {
                throw HttpException.PermissionDenied(error);
            }

            // load items - similar
            var result         = new AllInOneDto();
            var entityApi      = new EntityApi(appId, permCheck.EnsureAny(GrantSets.ReadDraft), Log);
            var typeRead       = entityApi.AppRead.ContentTypes;
            var list           = entityApi.GetEntitiesForEditing(items);
            var jsonSerializer = new 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();

            // todo: ensure that sub-properties of the content-types are included
            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
            result.Context = contextBuilder.InitApp(appIdentity.ZoneId, permCheck.App)
                             .Get(Ctx.AppBasic | Ctx.Language | Ctx.Site | Ctx.System);

            // done - return
            wraplog($"ready, sending items:{result.Items.Count}, " +
                    $"types:{result.ContentTypes.Count}, " +
                    $"inputs:{result.InputTypes.Count}, " +
                    $"feats:{result.Features.Count}");
            return(result);
        }
Example #2
0
 public Dictionary <Guid, int> Save([FromBody] AllInOneDto package, int appId, bool partOfPage)
 => new EditSaveBackend().Init(Log)
 .Save(GetBlock(), package, appId, partOfPage);
Example #3
0
        public Dictionary <Guid, int> Save(IBlock block, AllInOneDto package, int appId, 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  = new AppManager(appId, Log);
            var appRead = appMan.Read;
            var ser     = new JsonSerializer(appRead.AppState, Log)
            {
                // Since we're importing directly into this app, we would prefer local content-types
                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(block, 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");

            var publishing = new SxcPagePublishing().Init(block, Log);
            return(publishing.SaveInPagePublishing(appId, items, partOfPage,
                                                   forceSaveAsDraft => new EditSaveBackend().DoSave(appMan, items, forceSaveAsDraft),
                                                   permCheck));
        }