Search code examples
sitecorepage-editorsitecore7.1

Sitecore Page Editor Issues with Move and Add Components


I have a page with multiple components in one placeholder, different component types and also multiples of the same type. There are 3 components in a particular order on the page. It was deemed desirable to change the order. Selecting the component allows moving the component and it would appear to work correctly, until hitting Save, then it disappears. This is very similar to this SO posting from a while back here. I haven't found anything indicating this bug has been fixed. For reference I am using Sitecore 7.1 (rev.140324).

After this, an attempt to add a component of the same type to the page does not work either. After resetting the page to the initial state via presentation details it was apparent that adding a component to the placeholder was having issues to begin with. Right now the workaround is manipulating the page via presentation details, which isn't so helpful for content editors. The workaround entails entering the placeholder explicitly in the field, and nested where applicable, for all components on the page.

The Sitecore logs are not showing anything. The browser console shows "Cannot parse command parameters" twice after moving the component. The associated javascript:

this.parseCommandClick = function(commandClick) {
var msg = commandClick;
var commandParams = null;
var idx1 = commandClick.indexOf("(");
var idx2 = commandClick.indexOf(")");
if (idx1 >= 0 && idx2 > idx1) {
  msg = commandClick.substring(0, idx1);
  try {
    commandParams = $sc.evalJSON(commandClick.substring(idx1 + 1, idx2));
  }
  catch (e) {
    console.log("Cannot parse command parameters");
  }
}

return { message: msg, params : commandParams};

When the presentation details are set in the standard values for the page, the placeholder is not specified for some components and not nested for others. The issue can be replicated elsewhere by removing the specific placeholder of added components then moving them and saving. This causes the component to become deleted. I'm not sure the console logs directly relate to this.

I am trying to figure out why this could be happening.


Solution

  • It appears that it was how Sitecore works by default after all (as of 7.1), and not an issue with modifications and configuration. After a fresh instance was created, I was able to recreate the issue by adding components to the page via presentation details in the standard values. The issue arises from leaving the placeholder field empty. If this is the case then adding a component to the page gives the message: "An Error Occurred" and moving components often leads to deletion. So the fix for this is to change the standard values on all the effected items. It would appear there are interesting things happening when Sitecore is handling the empty fields. This is the approach I used to get around the issue.

    Sitecore support replied with an additional workaround to avoid data loss:

    Put the attached Layout Definition file into the \Website\sitecore\shell\Applications\Page Modes folder and clear the browser cache (replace your js file). This script prevents component removing and shows the following message: "The component is visually moved but some information could be lost if you move the component in PE. Please save the changes (reload if save is disabled) in order to see actual presentation and move the component via the Presentation Details (Advanced tab on the Ribbon)." Please note that this workaround was not deeply tested and we recommend you to backup your solution before applying any changes.

    LayoutDefinition.js

    Sitecore.LayoutDefinition = new function() {
    };
    
    Sitecore.LayoutDefinition.insert = function(placeholderKey, id) {
      var layoutDefinition = this.getLayoutDefinition();
      var device = this.getDevice(layoutDefinition);
    
      var r = new Object();
      r["@id"] = id;
      r["@ph"] = placeholderKey;
    
      device.r.splice(0, 0, r);
    
      this.setLayoutDefinition(layoutDefinition);
    };
    
    Sitecore.LayoutDefinition.getRendering = function(uid) {
      var layoutDefinition = this.getLayoutDefinition();
      var device = this.getDevice(layoutDefinition);
      if (!device) {
        return null;
      }
    
      for (var n = 0; n < device.r.length; n++) {
        if (this.getShortID(device.r[n]["@uid"]) == uid) {
          return device.r[n];            
        }
      }
    };
    
    Sitecore.LayoutDefinition.remove = function(uid) {
      var layoutDefinition = this.getLayoutDefinition();
      var device = this.getDevice(layoutDefinition);
    
      this.removeRendering(device, uid);  
      this.setLayoutDefinition(layoutDefinition);
    };
    
    Sitecore.LayoutDefinition.removeRendering = function(device, uid) {
      for (n = 0; n < device.r.length; n++) {
        if (this.getShortID(device.r[n]["@uid"]) == uid) {
          r = device.r[n];
          device.r.splice(n, 1);
          return r;
        }
      }
      return null;
    };
    
    Sitecore.LayoutDefinition.moveToPosition = function(uid, placeholderKey, position) {
      var layoutDefinition = this.getLayoutDefinition();
      var device = this.getDevice(layoutDefinition);
      var originalPosition = this._getRenderingPositionInPlaceholder(device, placeholderKey, uid);
    
      for (var totalCount = 0; totalCount < device.r.length; totalCount++)
      {
        var rendering = device.r[totalCount];       
        if (rendering["@ph"]=="")
        {
           window.alert('The component is visually moved but some information could be lost if you move the component in PE.  Please save the changes (reload page if save is disabled) in order to see actual presentation and move the component via the Presentation Details (Advanced tab on the Ribbon).');   
          return;
        }
      }
    
    
      var r = this.removeRendering(device, uid);
      if (r == null) {
        return;
      }
    
      r["@ph"] = placeholderKey;
    
      if (position == 0) {
         device.r.splice(0, 0, r);
         this.setLayoutDefinition(layoutDefinition);
         return;
      }
      // Rendering is moving down inside the same placeholder. Decrement the real position, because rendering itself is removed 
      // from his original position. 
      if (originalPosition > -1 && originalPosition < position) {
        position--;
      }
    
      var placeholderWiseCount = 0;
       var flag = true;
      for (var totalCount = 0; totalCount < device.r.length; totalCount++)
      {
        var rendering = device.r[totalCount];       
        if (Sitecore.PageModes.Utility.areEqualPlaceholders(rendering["@ph"], placeholderKey))
        {
          placeholderWiseCount++;
        }
    
        if (placeholderWiseCount == position)
        {    
          device.r.splice(totalCount + 1, 0, r);
          break;
        }
      }
    
      this.setLayoutDefinition(layoutDefinition);
    };
    
    Sitecore.LayoutDefinition.getRenderingConditions = function(renderingUid) {
      if (!Sitecore.PageModes.Personalization) {
        return [];
      }
    
      var layoutDefinition = this.getLayoutDefinition();
      var device = this.getDevice(layoutDefinition);
      var conditions = [];
      for (var i = 0; i < device.r.length; i++) {
        if (this.getShortID(device.r[i]["@uid"]) == renderingUid && device.r[i].rls) {
          var rules = device.r[i].rls.ruleset;
          if (rules && rules.rule) {
            if(!$sc.isArray(rules.rule)) {
              rules.rule = [rules.rule];
            }
    
            for (var j = 0; j < rules.rule.length; j++) {
              var conditionId = rules.rule[j]["@uid"];
              var isActive = Sitecore.PageModes.Personalization.ConditionStateStorage.isConditionActive(renderingUid, conditionId);
              conditions.push(new Sitecore.PageModes.Personalization.Condition(
                conditionId,
                rules.rule[j]["@name"],
                isActive
              ));
            }
          }
        }
      }
    
      return conditions;
    };
    
    Sitecore.LayoutDefinition.GetConditionById = function(conditionId) {
      var layoutDefinition = this.getLayoutDefinition();
      var device = this.getDevice(layoutDefinition);  
      for (var i = 0; i < device.r.length; i++) {
         var rules = device.r[i].rls ? device.r[i].rls.ruleset: null;
         if (rules && rules.rule) {
            if(!$sc.isArray(rules.rule)) {
              rules.rule = [rules.rule];
            }
    
            for (var j = 0; j < rules.rule.length; j++) {
              if (rules.rule[j]["@uid"] == conditionId) {
                return {rule : rules.rule[j]};
              }
            }
         }
      }
    
      return {};
    };
    
    Sitecore.LayoutDefinition.getRenderingIndex = function(placeholderKey, index) {
      var layoutDefinition = this.getLayoutDefinition();
      var device = this.getDevice(layoutDefinition);
    
      var i = 0;
    
      for (n = 0; n < device.r.length; n++) {
        if (device.r[n]["@ph"] == placeholderKey) {
          if (i == index) {
            return n;
          }
    
          i++;
        }
      }
    
      return -1;
    };
    
    Sitecore.LayoutDefinition.getRenderingPositionInPlaceholder = function(placeholderKey, uid) {
      var layoutDefinition = this.getLayoutDefinition();
      var device = this.getDevice(layoutDefinition);
      return this._getRenderingPositionInPlaceholder(device, placeholderKey, uid);
    };
    
    Sitecore.LayoutDefinition.getLayoutDefinition = function() {
      return JSON.parse($sc("#scLayout").val());
    };
    
    Sitecore.LayoutDefinition.setLayoutDefinition = function(layoutDefinition) {
      var newValue = $sc.type(layoutDefinition) === "string" ? layoutDefinition : JSON.stringify(layoutDefinition);
      if ($sc("#scLayout").val() != newValue) {
        $sc("#scLayout").val(newValue);
        Sitecore.PageModes.PageEditor.setModified(true);
      }
    };
    
    Sitecore.LayoutDefinition.getDeviceID = function() {
      return $sc("#scDeviceID").val();
    };
    
    Sitecore.LayoutDefinition.getDevice = function(layoutDefinition) {
      var deviceID = this.getDeviceID();
    
      if (!layoutDefinition.r.d) {
        return null;
      }
    
      //By serialization behaivour. If there is single element- it would not be serialized as array
      if (!layoutDefinition.r.d.length) {
        layoutDefinition.r.d = [layoutDefinition.r.d];
      }
    
      var list = layoutDefinition.r.d;
    
      for (var n = 0; n < list.length; n++) {
        var d = list[n];
    
        var id = this.getShortID(d["@id"]);
    
        if (id == deviceID) {
          //By serialization behaivour. If there is single element- it would not be serialized as array
          if (d.r && !d.r.length) {
            d.r = [d.r];
          }
          return d;
        }
      }
    
      return null;
    };
    
    Sitecore.LayoutDefinition.getShortID = function(id) {
      return id.substr(1, 8) + id.substr(10, 4) + id.substr(15, 4) + id.substr(20, 4) + id.substr(25, 12);
    };
    
    Sitecore.LayoutDefinition.readLayoutFromRibbon = function() {
      var layout = Sitecore.PageModes.PageEditor.ribbon().contentWindow.$("scLayoutDefinition").value;    
      if (layout && layout.length > 0) {
        this.setLayoutDefinition(layout);
        return true;
      }
    
      return false;
    };
    
    Sitecore.LayoutDefinition._getRenderingPositionInPlaceholder = function(device, placeholderKey, uid) {
      var counter = 0;
      for (var i = 0; i < device.r.length; i++) {
        if (Sitecore.PageModes.Utility.areEqualPlaceholders(device.r[i]["@ph"],placeholderKey)) {
          if (this.getShortID(device.r[i]["@uid"]) == uid) {
            return counter;
          }
    
          counter++;
        }
      }
    
      return -1;
    };