Problems with Edit Placement from Admin

Topics: Core, Customizing Orchard
Sep 21, 2013 at 11:14 PM
When I change placement under Edit Placement button in content type definition and press Save I've got the error
An unhandled exception has occurred and the request was terminated. Please refresh the page. If the error persists, go back
Object reference not set to an instance of an object.
System.NullReferenceException: Object reference not set to an instance of an object. at Orchard.ContentTypes.Controllers.AdminController.EditPlacementPost(String id, EditPlacementViewModel viewModel) in c:\...\src\Orchard.Web\Modules\Orchard.ContentTypes\Controllers\AdminController.cs:line 176 at lambda_method(Closure , ControllerBase , Object[] ) at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) at ... 
It happens as you can see in the POST-action in the Orchard.ContentTypes
[HttpPost, ActionName("EditPlacement")]
        [FormValueRequired("submit.Save")]
        public ActionResult EditPlacementPost(string id, EditPlacementViewModel viewModel) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(id);

            if (contentTypeDefinition == null)
                return HttpNotFound();

            var allPlacements = _placementService.GetEditorPlacement(id).ToList();
            var result = new List<PlacementSettings>(contentTypeDefinition.GetPlacement(PlacementType.Editor));

            contentTypeDefinition.ResetPlacement(PlacementType.Editor);

                foreach (var driverPlacement in viewModel.AllPlacements) {
                    ....
                    }
                }
The problem is that viewModel is empty and the exception is raised on the line
 foreach (var driverPlacement in viewModel.AllPlacements)
because viewModel.AllPlacements is null.
It is not clear for me what is going on there. The model is correct on Get action and something breaks it after that.
I try to do the same on a clean install with my modules disabled and it seems it works but I don't realise how my module can affect Orchard.ContentTypes behaviour. Please any ideas of possible reasons for it?
Sep 23, 2013 at 2:54 PM
Please, help!
Coordinator
Sep 23, 2013 at 5:39 PM
File a bug with precise repro steps.
Sep 25, 2013 at 10:09 PM
Edited Sep 25, 2013 at 10:10 PM
I have found the reason for the bug in Orchard.ContentTypes. It is in Orchard.ContentTypes\Views\Admin\EditPlacement.cshtml file.

When we change placement we change the order of those 'placement' items ( and underlying input elements). In order for MVC binding to work properly the id/names of input elements must be an unbroken sequence of integers starting at 0 and increasing by 1 for each element. Sometimes it is not the case at all. And the script in EditPlacement.cshtml is changing 'position' input element value only but id/names must be corrected as well.
So the next change is working for me.
Instead of Foot script function
    (function ($) {
        var assignPositions = function () {
            var position = 1;
            $('.position').each(function() {
                $(this).val(position++);
            });
        };
       assignPositions();
        var startPos;

        $('#placement').sortable({
            placeholder: "placement-placeholder",
            start: function (event, ui) {
                var self = $(ui.item);
                startPos = self.prevAll().size();
            },
            stop: function (event, ui) {
                assignPositions();
                $('#save-message').show();
            }
        });
        $('.shape-editor *').attr('disabled', 'disabled');
        $("#placement").disableSelection();
    })(jQuery);
should be something like this
    (function ($) {
      var assignPositions = function () {
            var position = 0;
            $('.type').each(function () {
                var input = $(this);
                reAssignIdName(input, position);  // type

                input = input.next();
                reAssignIdName(input, position);  // differentiator

                input = input.next();
                reAssignIdName(input, position);  // zone

                input = input.next();
                reAssignIdName(input, position);  // position

                input.val(++position);
            });
        };

        var reAssignIdName = function (input, pos) {
            var name = input.attr('name');
            input.attr('name', name.replace(new RegExp("\\[.*\\]", 'gi'), '[' + pos + ']'));

            var id = input.attr('id');
            input.attr('id', id.replace(new RegExp('_.*__', 'i'), '_' + pos + '__'));
        };

        assignPositions();
        var startPos;

        $('#placement').sortable({
            placeholder: "placement-placeholder",
            start: function (event, ui) {
                var self = $(ui.item);
                startPos = self.prevAll().size();
            },
            stop: function (event, ui) {
                assignPositions();
                $('#save-message').show();
            }
        });

        $('.shape-editor *').attr('disabled', 'disabled');
        $("#placement").disableSelection();
    })(jQuery);
I have filed the bug as well https://orchard.codeplex.com/workitem/20154.
Feb 23, 2014 at 2:14 PM
I have submitted this on the bug report page but I thought it was relevant here as well. Whilst applying the above update generally fixed the error for me, it doesn't appear to work for all fields/parts. I haven't narrowed down the cause yet but I still get the following issues:
  1. If I place a Parts_Widget_WidgetPart as the first item then I still get the error. When I move the same part to second (or any other position) the placement saves okay with no error.
  2. Moving a Fields_MediaLibraryPicker field is completely ignored. The page saves with no error but when reloading the page the field's ordering is the same as what it was before.