Unexpected Response from Server when updating SharePoint ListItem via JSOM

These days I am working a lot on the client-side of things. So I starting a couple of months ago writing my first lines of JavaScript with JSOM (Javascript (Client)-Side Object Model).

I wrote a small method to create list items in a list (listTitle) based on a collection/ list of properties (properties) and their respective values. Here it is:

function createListItem(listTitle, properties) {
    var d = $.Deferred();
    try {
        var ctx = SP.ClientContext.get_current();
        var oList = ctx.get_web().get_lists().getByTitle(listTitle);

        var itemCreateInfo = new SP.ListItemCreationInformation();
        oListItem = oList.addItem(itemCreateInfo);

        for (var i = 0; i < properties.length; i++) {
            var prop = properties[i];
            oListItem.set_item(prop.Key, prop.Value);
        }

        oListItem.update();
        ctx.load(oListItem);
        var o = { d: d, ListItem: oListItem, List: listTitle };
        ctx.executeQueryAsync(
        function () {
            o.d.resolve(o.ListItem);
        },
        function (sender, args) {
            o.d.reject("Could not create list item in list " + o.List + " - " + args.get_message());
        });
    } catch (Exception) {
        console.log(Exception.message);
    }
    return d.promise();
}

function updateListItem(listTitle, properties) {
  var d = $.Deferred();
  try {
        var d = $.Deferred();
        var ctx = SP.ClientContext.get_current();
        var oList = ctx.get_web().get_lists().getByTitle(listTitle);

        oListItem = oList.getItemById(id);

        for (var i = 0; i < properties.length; i++) {
            var prop = properties[i];
            try {
                oListItem.set_item(prop.Key, prop.Value);
            } catch (Exception) {
                console.log(prop.Key + ' ' + prop.Value);
                console.log(Exception.message);
            }
        }
        oListItem.update();
        //ctx.load(oListItem);
        var o = { d: d, ListItem: oListItem, List: listTitle, p: properties };
        ctx.executeQueryAsync(
            Function.createDelegate(o, 
        function () {
            o.d.resolve(o.ListItem);
        }),
        Function.createDelegate(o, 
        function (sender, args) {
            o.d.reject("Could not update list item in list " + o.List + " - " + args.get_message());
        }));
    } catch (Exception) {
        console.log(Exception.message);
    }
  return d.promise();
}

So what happened when I executed while editing another item in a different list? When I debugged using Chrome (my browser of choice when writing JavaScript – never used it before that, interestingly…) I received an error “unexpected response from the server”.

I figured out that there are two key lines in this code.

var ctx = SP.ClientContext.get_current();

and

ctx.load(oListItem);

The first line was actually not responsible for the error message in my case but if you use var ctx = new SP.ClientContext(url); you may encounter this error message, as well. You should always use the current client context, when using JSOM, similar to the best practice guidelines for opening webs on server-side (SSOM [Server-Side Object Model]).

The second line was actually the issue in my code.

When creating an item I need to load the item into the context afterwards (or the error will show up even if the item is created correctly).

When updating an item the item may not be loaded into the context afterwards (or the error will show up even if the item is updated correctly).

It kind of makes sense, because when creating an item you are actually sending an SP.ListItemCreationInformation to the server. When updating an item I already have my listitem object. Why would I need to load all the other information afterwards?

So once I removed the line from the update method the code no longer evaluated to fail and the error message disappeared.

So for the experts among you this may be old news, but I actually needed to think about this for a few minutes before I figured it out, so I thought it was well worth blogging about. Especially since I haven’t blogged for quite some time.

Advertisements

AppManagement and SubscriptionSettings Services, Multiple Web Applications and SSL

So currently I am setting up four environments of which one is production, 2 are staging and another is was a playground installation.

My staging environments (TEST, QA, PROD) are multi-server, multi-farm systems (multi-farm because the 2013 Farm publishes Search and UPA to existing 2010 Farms).
They are running SPS2013 Standard with March PU 2013 + June CU 2013. They will be using App Pool Isolation and App Management and SubscriptSettings Services have their own account (svc_sp{t, q, p}_app, i.e. svc_spt_app, svc_spq_app and svc_spp_app).

I have three web applications of which all are secured by SSL making a wildcard certificate necessary for the app domain. Each has their own account (svc_sp{t, q, p}_{col, tws, upa}). The reason for this is that I will be using Kerberos Authentication and for the SPNs I need dedicated accounts for each Application URL.

My playground was once a 4 server farm, but now 3 servers have been removed. It does not run the March PU 2013 nor June CU 2013. There app pool isolation wihtout SSL is used.

On the playground the app management worked well. I actually encountered my problem on my test first and tried to replicate on the playground, but couldn’t. But I am getting ahead of myself. The system was setup via autospinstaller and the necessary certificates and IPs involved were requested and implemented. The AD Team did the domain setup for me. I didn’t setup my environment following this article, but it is a good one to read. I also got the idea of creating a separate dummy web application for attaching my IIS Bindings and Certificate from it, which makes a lot of sense, because of security considerations and kerberos.

The first article to read to get an overview of what is necessary and what’s trying to be achieved can be found here.

So I set up everything and still it wasn’t working. What does that mean? I will explain. When I subscribe to an app, download it from the store and add it in a site collection of my choosing I get to click on it once it is finished installing. The link then leads me to my app domain. With SSL only when I was using the same application pools I could actually get anywhere, otherwise I say the below.

This is what I wanted to see:
Expected

This is what I saw on any of the web applications with SSL and that had a different app pool account than the one I was using for my dummy web application.
Actual

So this blank page is actually exactly what you see when you leave the request management service running on the frontends without doing any topology configuration.

So I tried to work with the user policy from the web application management page in hopes of giving the users permissions on the content databases. This was actually not happening as I found out later, but which was actually exactly what was needed. I had to manually add the account of the app pool for the app domain to the SPDataAccess Group of the content databases. Then it also works with SSL. I actually set up three web applications WITHOUT SSL on the Test Staging Environment with the same users as the SSL Web Applications and this worked like a charm, but for any SSL web application I needed to explicitly give permissions to the content database. This is a nightmare to maintain. For my migration of 20 databases from 2010 to 2013 I need to do this again and again and for each new content database I will create in the future. Just imagine you create a new content database and forget to do this. Now for any site collection in this content database the above issue will show up. Hard to debug later on.

Not sure what Microsoft is thinking here, but I am happy that it only took me 4 days to figure this one out.

web.config

Okay, so I have my dev machine, and I have the typical error: please change your setting of customError in the web.config to RemoteOnly or Off so you can see the error.

I tried a lot, but this is what finally did the trick:
change the setting in the 14 hive web.config:
C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\web.config

– I checked to make sure the spelling was right (it is case-sensitive).
– I added it at different parts of the config file (no difference, you will see there is only one correct place to put it).
– Changed it from Off to RemoteOnly to Off (no difference, if you are accessing locally)
– Changed it via web.config manually and via IIS (the settings seem to be synced, but in special cases it seems they are not).

Creating a UPA Proxy via PowerShell – technet article typo

http://technet.microsoft.com/en-us/library/ff607737.aspx

In the example it says:

$app = Get-SPServiceApplication -Name PartitionedUserProfileApplication. New-SPProfileServiceApplicationProxy -Name PartitionedUserProfileApplication_Proxy -ProfileServiceApplication $app -PartitionMode

But the Member is -ServiceApplication rather than -ProfileServiceApplication.

Tried it, worked. Keep in mind.

If they had a chance to add a comment, I would have done it there.

CheckedIn / Added events on libraries

So I had to do another event receiver for a wiki pages library. There are different versions depending on the development state – they are different in that the library either has a force checkout or not. See below image and then the lower of the two red rectangles shows the setting that is either yes or no. The other one defines if a simple approval workflow is used to manage versions.
Versioning Settings

So, what am I doing in my eventreceiver? I am adding the title, if it was not set (watch out, there is a difference between the dialog for a new page via the site settings menu and the new item in the pages library. The other thing I am doing is changing the settings of XsltListViewWebParts (change the view, set the row limit to at least 100 and filter the view using the title and a column I use called ‘page’.

These are the two interesting parts of the code:

This one sets the title. That means I need a web opened as an administrator and then check to see if the title is the way I want it to be…

So I get the list and the item and then check the title field.
if the title is null, then I set it, item.Title does not have a set-method so there we go…quick question what is faster? GetItemById, or GetItemByUniqueId? I would expect the first option…I did try it once though, and the id returned 33 while the number of items was 13 (I deleted a lot of pages) and the method returned an error, so I was playing safe here.

      private void SetTitle(SPItemEventProperties properties)
        {
            SPListItem item = properties.ListItem;
            if (properties.Web != null)
            {
                SPSecurity.RunWithElevatedPrivileges(
                    delegate
                    {
                        using (SPSite site = new SPSite(properties.Web.Url))
                        using (SPWeb web = site.OpenWeb())
                        {
                            SPList list = web.Lists[properties.ListId];
                            SPListItem admItem = 
                                           list.GetItemByUniqueId(item.UniqueId);
                            if (string.IsNullOrEmpty(
                                admItem[SPBuiltInFieldId.Title] as string)
                            )
                            {
                                title = admItem.Name.Replace(".aspx", "");
                                admItem[SPBuiltInFieldId.Title] = title;
                            }
                            else 
                            {
                                title = item.Title;
                            }
                            EventFiringEnabled = false;
                            site.AllowUnsafeUpdates = true;
                            web.AllowUnsafeUpdates = true;
                            admItem.SystemUpdate(false);
                            web.AllowUnsafeUpdates = false;
                            site.AllowUnsafeUpdates = false;
                            EventFiringEnabled = true;
                        }
                    });
            }
        }

The event firing and the updating is just fine right there. Watch for the system update, which is crucial.

Problem right now is still, that the check-in will overwrite the modified-by user that did the check-in that triggered the event, that I will solve with <a href="this.

     private void CheckView(SPWeb web, XsltListViewWebPart wp)
        {
            Guid oGuid = new Guid(wp.ViewGuid);
            SPList list = web.Lists[wp.ListId];
            SPView oWebPartView = list.Views[oGuid];
            int limit = 100;
            bool changed = false;
            if (oWebPartView.RowLimit < limit)
            {
                oWebPartView.RowLimit = 
                        Math.Max(limit, oWebPartView.RowLimit);
                changed = true;
            }
            string query = "" + title + "";
            if(!query.Equals(oWebPartView.Query))
            {
                oWebPartView.Query = query;
                changed = true;        
            }
            if(changed)
                oWebPartView.Update();
        }

In this method I simply take the webpart, which was already casted, then I pick up the Guid and get the view from the list. Initially I thought: what if somebody has an XsltListViewWebPart referring to a list in a sub-web…well that’s not possible – I checked – at least not ootb.

So now that I developed these two functions for the check-in event I thought: what if there is no force-checkout? Well that I still need to implement, adding an added-event and having it work only if there is a valid item to work on, because the properties.ListItem member does not exist after the added event when force-checkout is in-place.
So you got to watch that!

        private SPLimitedWebPartManager GetManagerFromItem(SPListItem item)
        {
            if (item == null || item.File == null) return null;
            SPFile itemFile = item.File;
            // open file...
            // GetLimitedWebPartManager from SPFile object
            SPLimitedWebPartManager wpmgr = 
                         itemFile.Web.GetLimitedWebPartManager(
                         itemFile.ServerRelativeUrl, 
                         PersonalizationScope.Shared);
            return wpmgr;
        }
        ...
        SPLimitedWebPartCollection collection = wpmngr.WebParts;
        List consumers = new List();
        foreach (var wp in collection)
        {
          if (wp != null)
          {
            if (wp is XsltListViewWebPart)
            {
              XsltListViewWebPart view = (XsltListViewWebPart) wp;
              CheckView(web, view);
              consumers.Add(view);
            }
          }
        } 
        ...                        

So what happens here is just that I get the limited webpart manager from the spfile object’s relativeurl and then iterate over the webpartcollection getting only the xsltlistviewwebparts…this is also discussed at many other places, you can google that, if you need more in-depth information.

The trouble with editing items on synchronous add-events

As I have written once or twice (see here) before, I create a lot of functionality using event receivers.

Now when you are adding new items you might want to fill field values based on either the event or external data, so you will use a synchronous adding event (SPEventReceiverType.ItemAdding). Once you get the item (SPListItem item = properties.ListItem) and you want to edit the item you should use a disable-event-firing strategy (Eventfiring) if appropriate (trigger event on edit, not insert) – this is usually appropriate when you know conditions to be false on insertion or if you already call methods from insert events that would be duplicated by the edit-event.

Now the tricky part of the editing is that if you do it wrong, you will get cryptic messages (“Microsoft.SharePoint.SPException: Save Conflict Your changes conflict with those made concurrently by another user. If you want your changes to be applied, click Back in your Web browser, refresh the page, and resubmit your changes.”). The reason for this is that the user who creates the item is not the same as the context the event receiver is running in as long as you use the standard edit-and-update-methods available.

So let’s do it the right way:

SPListItem item = properties.ListItem;
item["TheInternalFieldName"] = "TheCalculatedValue";
item.SystemUpdate(false);

So this little piece of code took me a long time to figure out. Let’s check out why it’s good and then, what you could have done wrong:
The first step is basic: get the item from the properties.
The second step is editing the item. Easy! The internal field name must exist and the calculated value should be something that fitts into the column. Bamm! There you go. You could have done this by yourself so far.
Now, here is the important part: item.SystemUpdate(false).
It’s a system-user update with false as parameter.
If you check msdn you will find there are two SystemUpdate-methods. Web-Objects have the Update (non-overloaded: Update()), List-Objects have the method Update (overloaded: Update(), Update(boolean)) and items also have SystemUpdate (overloaded: SystemUpdate(), SystemUpdate(boolean)).
SystemUpdate(false) is exactly what you want as the explanation says: Updates the database with changes that are made to the list item without changing the Modified or Modified By fields, or optionally, the item version.
Okay. So you’re safe. One thing less to worry about. But why? Why? If it’s that simple why am I writing a whole article dedicated to this?
Well I have seen the error-messages. I need to reproduce them, because it has been a while since then but the essence is: usually you use update, as it is natural for lists and web and you use it without parameter, because usually you don’t have it (web) or you don’t need it (list – only when migrating data). So until you understand why SystemUpdate with boolean is important, a whole day can go by.

Basically I’m saying the same as Karine Bosch. Just found it while searching for error messages!

Defining and Connecting Information (Expiration) Policies

Recently I came across the requirement to add a retention policy (expiration policy) to a content type in the context of a contract management solution. So I created a document library and content types for different types of contracts. The content types in turn can have so called policies, i.e. policies can be attached to the content types. Once attached, these policies call a specified workflow, once a condition is fulfilled. This condition may be fulfilled once a certain date is reached, i.e. a timespan has passed since a specified date in time. The timespan may be one of these: (“years”, “months”, “days”)

The docLib, the and the cTypes that was easy. Connecting the policy was what I had to research. So let’s just assume that has been done and focus on the interesting stuff.


SPWeb web = null; // ... the web, init using either SPContext or using-statement from SPSite Object
string name = "MyContentNameInWeb"; // ... the name of the content type
string workflowName = "MyWorkflowInWeb"; // ... the name of the workflow
// this is a general method for retrieving the content type from the web by name
SPContentType cType = GetContentTypeByName(web, name);
SPWorkflowTemplate tpl = GetWorkflowByName(web, workflowName); // ... get a workflow from the web context

So the code above is plain setting-up. Get a web…I don’t care how you do it. Get the content type you want the policy to be attached to and now get ready to create / attach it:


///
/// Gets the customized custom data for a workflow triggering.
///
/// The workflow template that is subsequently triggered.
/// The time span ('days', 'months', 'years').
/// The number of timeSpans.
///
public static string GetCustomData(SPWorkflowTemplate tpl, String timeSpan, int numberOf)
{
return @"<data><formula id=""Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration.Formula.BuiltIn""><number>"
+ numberOf
+ "</number><property>Created</property>"
+ "<period>"
+ timeSpan
+ "</period>"
+ @"</formula><action type=""workflow"" id="""
+ GuidFacade.GetGuidString(tpl.Id)
+ @""" /></data>";
}

The code you see is a method for getting an xml-string which defines when and what is triggered by the policy. Notice the formula sub-tree which specifies the condition. The id is essential here as it defines the way the information given via the xml is processed. Notice that the name is a qualified name: Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration.Formula.BuiltIn. So if you want to read up on information policies on msdn that is where to start looking. The dll which contains the policy logic is prefixed with Microsoft.Office. It is not part of the standard Microsoft.SharePoint.dll. Also notice the text-element for the tag <property>. This is the field where the date is stored, that is used for evaluation if the condition is fulfilled. In this case it is the standard field, which is associated to every list (even basic custom list): ‘created’.
You can also see that the value of action type is ‘workflow’, so there are some other types of actions as well, but they are not important in this scenario.

The guidfacade gets a specially formatted guid-string (g.ToString("B").ToUpper()).


string customData = GetCustomData(tpl, "years", 5);
if (Policy.GetPolicy(cType) == null)
{
//if the content type hasn't got a Policy yet, create a new Policy
Policy.CreatePolicy(cType, null);
}

Policy policyOfContentType = Policy.GetPolicy(cType);
policyOfContentType.Name = policyName;
string policyFeatureId = “Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration”;
//Add expiration policy to the content type
if (policyOfContentType.Items[policyFeatureId] == null)
{
policyOfContentType.Items.Add(policyFeatureId, customData);
}
web.Update();

This last part of code gets the customData from the method of the second code block and checks if a policy is associated with the content type. if not, then it is subsequently created. At this point it is important to remind ourselves that there is a difference between a content type associated to a list or docLib and a content type of a web context. Different policies may be attached to same content types associated with different objects, i.e. a basic content type that is created and therefore attached to the web context may be attached to a list or docLib and afterwards a policy may be added to either one without the policy beeing attached to the other one. That has major implications for the order of statements (attach to list, attach policy != attach policy, attach to list).

If you want the policy to be attached to the content type of the list, then you must use the content type of the list to look-up the according policy (Policy policy = Policy.GetPolicy(cTypeOfList)).

In the end check if there is an expiration policy associated already, if not then add the new expiration. In this case it’s just about vanilla if you check first, my scenario needed an add-no-edit strategy, you can also use a replace-and-give-notice strategy or an add-and-edit strategy if you like.