Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Bo Jacobsen 597 posts 2395 karma points
    Dec 14, 2023 @ 15:09
    Bo Jacobsen
    0

    DTGE preview render broke after upgrading Umbraco CMS to 10.8.0 +

    Hi Søren.

    I know the package is retired, but I hope you can find the time to look at this issue.

    Our solutions that we upgraded to Umbraco CMS 10.8.0 + (also the new security update 10.8.2) cannot render previews when submitted to the GRID element, then we get this error:

    Possibly unhandled rejection: {"data":{"ExceptionMessage":"Object reference not set to an instance of an object.
    ","ExceptionType":"System.NullReferenceException, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","StackTrace":"
    at Our.Umbraco.DocTypeGridEditor.Models.UnpublishedContent..ctor(IContent content, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, PropertyEditorCollection propertyEditorCollection, IPublishedContentTypeFactory publishedContentTypeFactory)
    at Our.Umbraco.DocTypeGridEditor.Models.UnpublishedContent..ctor(Int32 id, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, PropertyEditorCollection propertyEditorCollection, IPublishedContentTypeFactory publishedContentTypeFactory)
    at Our.Umbraco.DocTypeGridEditor.Controllers.DocTypeGridEditorApiController.GetPreviewMarkup(PreviewData data, Int32 pageId)
    at lambda_method2733(Closure , Object , Object[] )
    at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
    --- End of stack trace from previous location ---
    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
    --- End of stack trace from previous location ---
    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
    "},"status":500,"config":{"method":"POST","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"/umbraco/backoffice/DocTypeGridEditorApi/DocTypeGridEditorApi/GetPreviewMarkup?dtgePreview=1&pageId=4087","data":"id=ad2eada8-4c96-1823-b5a3-bf71177338a4&editorAlias=Synergi.Grid.Image&contentTypeAlias=ImageDoc&value=%7B%22name%22%3A%22Image%22%2C%22image%22%3A%5B%7B%22key%22%3A%22f39fa02c-e567-42f3-bcc6-a81f7e94eac3%22%2C%22mediaKey%22%3A%229d0f2af9-5d43-46f9-91e8-190f19ceab0e%22%2C%22crops%22%3A%5B%5D%2C%22focalPoint%22%3A%7B%22left%22%3A0.5%2C%22top%22%3A0.5%7D%7D%5D%2C%22alt%22%3A%22%22%2C%22caption%22%3A%22%22%2C%22crop%22%3A%22%22%2C%22fullWidth%22%3A%220%22%2C%22linkProperty%22%3A%5B%7B%22key%22%3A%22c0f8b34d-db3b-4076-bcfd-e61aaa8079a1%22%2C%22name%22%3A%22Link%22%2C%22ncContentTypeAlias%22%3A%22LinkAriaLabelPropertyItem%22%2C%22PropType%22%3Anull%2C%22link%22%3A%5B%5D%2C%22ariaLabel%22%3A%22%22%7D%5D%7D&viewPath=%2FViews%2FPartials%2FDocTypeGridEditor%2F&previewViewPath=%2FViews%2FPartials%2FDocTypeGridEditor%2FPreviews%2F&culture=","headers":{"Content-Type":"application/x-www-form-urlencoded","Accept":"application/json, text/plain, */*","X-Requested-With":"XMLHttpRequest","X-UMB-CULTURE":"da-DK","X-UMB-SEGMENT":null,"X-UMB-XSRF-TOKEN":"CfDJ8JYPdkIDq79Llg7XWIOAjE1cu7pXj1W_k_SMjooUfAF2LRxtIK3enH2rhSwae_Q1r6xXoHh_YCUOsk7nNPFTNy7L6hGoyLIu7-YVM9KrgecSYCtW2Es0jvF4YeL639kGQT0rUM-fuButkDZIdj7QININnREguFymjCL-YD6h6O9CHL74Wx54bRAlgLItC12moA"}},"statusText":"","xhrStatus":"complete"}
    

    This error is from our Synergi.Grid.Image Grid Editor. But we get the same error with all our Grid Editors that uses DTGE.

  • Bo Jacobsen 597 posts 2395 karma points
    Dec 21, 2023 @ 13:15
    Bo Jacobsen
    0

    After some diggin, ive found out that the editorState.current.id is not returning the right id when the $scope.setPreview is called inside Our.Umbraco.DocTypeGridEditor.GridEditors.DocTypeGridEditor.

    So when the DocTypeGridEditorApiController method GetPreviewMarkup is checking for _contentQuery.Content(pageId) with the wrong pageId then it thinks that its unpublished because its returning null and is making a new UnpublishedContent which leads to a null reference in the contructor

    public UnpublishedContent(int id, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, PropertyEditorCollection propertyEditorCollection, IPublishedContentTypeFactory publishedContentTypeFactory)
        : this(contentService.GetById(id), contentService, contentTypeService, dataTypeService, propertyEditorCollection, publishedContentTypeFactory)
    

    on contentService.GetById(id) where the id is the wrong pageId.

    However when the view is loading in the backoffice for rendering the first time then the editorState.current.id is the right one.

  • Bo Jacobsen 597 posts 2395 karma points
    Dec 22, 2023 @ 09:20
    Bo Jacobsen
    0

    I found the course to the issue.

    When the controller Our.Umbraco.DocTypeGridEditor.Dialogs.DocTypeGridEditorDialog is calling the function submit. The function use contentEditingHelper.contentEditorPerformSave(args).

    // save the content as a blueprint, to trigger validation
    var args = {
        saveMethod: contentResource.saveBlueprint,
        scope: $scope,
        content: $scope.model.node,
        create: true,
        action: "save",
        showNotifications: false,
        softRedirect: true
    }
    

    The contentEditingHelper method contentEditorPerformSave is updating the editorState with editorState.set(args.content) which leads to wrong editorState data for everything that is using the editorState service.

    contentEditorPerformSave: function (args) {
                if (!Utilities.isObject(args)) {
                    throw "args must be an object";
                }
                if (!args.scope) {
                    throw "args.scope is not defined";
                }
                if (!args.content) {
                    throw "args.content is not defined";
                }
                if (!args.saveMethod) {
                    throw "args.saveMethod is not defined";
                }
                if (args.showNotifications === undefined) {
                    args.showNotifications = true;
                }
                // needed for infinite editing to create new items
                if (args.create === undefined) {
                    if ($routeParams.create) {
                        args.create = true;
                    }
                }
                if (args.softRedirect === undefined) {
                    //when true, the url will change but it won't actually re-route
                    //this is merely here for compatibility, if only the content/media/members used this service we'd prob be ok but tons of editors
                    //use this service unfortunately and probably packages too.
                    args.softRedirect = false;
                }
    
    
                var self = this;
    
                //we will use the default one for content if not specified
                var rebindCallback = args.rebindCallback === undefined ? self.reBindChangedProperties : args.rebindCallback;
    
                var formSubmitOptions = { scope: args.scope, action: args.action };
                if(args.skipValidation === true) {
                    formSubmitOptions.skipValidation = true;
                    formSubmitOptions.keepServerValidation = true;
                }
                if (formHelper.submitForm(formSubmitOptions)) {
    
                    return args.saveMethod(args.content, args.create, fileManager.getFiles(), args.showNotifications)
                        .then(function (data) {
    
                            formHelper.resetForm({ scope: args.scope });
    
                            if (!args.infiniteMode) {
                                self.handleSuccessfulSave({
                                    scope: args.scope,
                                    savedContent: data,
                                    softRedirect: args.softRedirect,
                                    rebindCallback: function () {
                                        rebindCallback.apply(self, [args.content, data]);
                                    }
                                });
    
                                //update editor state to what is current
                                editorState.set(args.content);
                            }
    
                            return $q.resolve(data);
    
                        }, function (err) {
    
                            formHelper.resetForm({ scope: args.scope, hasErrors: true });
    
                            self.handleSaveError({
                                showNotifications: args.showNotifications,
                                softRedirect: args.softRedirect,
                                err: err,
                                action: args.action,
                                rebindCallback: function () {
                                    // if the error contains data, we want to map that back as we want to continue editing this save. Especially important when the content is new as the returned data will contain ID etc.
                                    if(err.data) {
                                        rebindCallback.apply(self, [args.content, err.data]);
                                    }
                                }
                            });
    
                            //update editor state to what is current
                            editorState.set(args.content);
    
                            //needs to be manually set for infinite editing mode
                            args.scope.isNew = args.content.id === 0 && args.scope.isNew;
    
                            return $q.reject(err);
                        });
                }
                else {
                    return $q.reject();
                }
    
            }
    

    Could DTGE trigger a validation another way?

  • Bo Jacobsen 597 posts 2395 karma points
    Dec 22, 2023 @ 09:45
    Bo Jacobsen
    0

    By adding infiniteMode: true to the args, we can prevent the editorState.set(args.content) to be called if the args.saveMethod isnt returning an error.

Please Sign in or register to post replies

Write your reply to:

Draft