TagSharePoint

The Mystery of the Empty Search Result Fields

Today my colleague Roel and I ran into a problem that had us baffled. To set the scene; we were migrating a news aggregation platform to Office 365 consisting of multiple site collections that aggregate news from each other based on SharePoint Search. Each of these site collections is practically identical but strangely enough on some of them the search index was returning one specific field as empty even after multiple re-indexings.

To fast-forward through a few hours of utter frustration; at one point we remembered that we had practically the same problem before but on an on-premise SharePoint environment. The solution back then was to modify the SourceID in the SchemaXML of the field. The SourceID is a property that defines where a field originates (e.g. if it is a built-in field or a site column). Below is an example of what you might find in your field’s SchemaXML:

<Field 
   Type="Note" 
   DisplayName="MainText"
   [...] 
   SourceID="{a5a7f91f-b3c6-40e1-ab3d-0499544b8f52}" 
/>

The GUID in this example can refer to either a List ID or a Web ID – and as we found out this makes all the difference. On the sites where the field was being indexed, the SourceID referred to the Web ID and on the sites where the field remained empty the field referred to the List ID. While it’s not at all clear where this ID is used in SharePoint Search (or if it’s even used at all), I suspect that if the field is a site column, the SourceID must always refer to the Web ID instead of the List ID.

After we changed the SchemaXML of the faulty fields and scheduled a re-index of the List the contents were finally being indexed again and we sighed a collective breath of relief.

Pro Tip: where did my deleted SharePoint Hosted Add-In listitem go to?

A client for whom we had built a SharePoint Hosted Add-in consisting of multiple inter-connected SharePoint lists, deleted a list item from one of the lists. This list item was used in a couple of the other lists as a lookup field. The problem with this constellation is that you cannot simply recreate the same list item and hope for the lookup field to be fixed, as this connection relies on the list item’s ID. Unfortunately though, you cannot set the ID field for a new list item.

The solution seemed to be simple however: open up the recycle bin for the Add-in (Add-in URL plus ‘_layouts/15/RecycleBin.aspx’), find the deleted item and recover it. We didn’t immediately realize that a SharePoint Hosted Add-in does not contain a recycle bin!

After some consideration we figured that we should check the recycle bin of the site containing the Add-in, and whaddayaknow, there was the missing list item! We could simply recover the list item from the recycle bin, restoring all of the broken relationships in the SharePoint Hosted Add-in as well. Come to think about it: the name ‘SharePoint Hosted Add-in’ kind of gives it away already: check the containing site for stuff that’s missing!

What happened to the SP Online Social REST API?

Last month I encountered a strange issue with the SharePoint Online REST API when trying to implement a feature for a social intranet we are building for one of our customers. The feature depends on the SocialRestFollowingManager resource, as described on this page on docs.microsoft.com.

I had already used this particular piece the REST API in other projects in SharePoint 2013 Server using the client-side coding library. Everything seemed to be working at first sight, as I could leverage this functionality to follow objects in SharePoint Online: sites, tags and documents.

The snag I hit was when I was trying to follow and unfollow documents specifically. As we have probably all noticed, the following of a single document from a document library has disappeared from the context menus in the SharePoint Online modern experience. This might already be an indication that Microsoft is not really willing to continue supporting this otherwise brilliant feature. We have had a lot of positive customer feedback on our implementation of this feature, where we can create a centrally located pinboard containing the direct links to all of the SharePoint content that you are following.

Whatever! Dive in to the code already!

When we take a look at the code, the funny thing is that it seems to work at first. The following of a document is done through the SharePoint UI: by switching a SharePoint document library to the classic view and using the context menu to follow the item (‘Volgen’ is Dutch for ‘Follow’):

Now for the ‘unfollowing’:

We use the function stopFollowDocument  to unfollow this document. In this function, we create an async ajax call to the social REST API, located under /_api/social.following containing the json-ized object (our document’s information) that needs to be unfollowed. The call is executed and the ajax call proceeds to its ‘success’-function. So far so good! But when we proceed to check if the document is now ‘unfollowed’, which we can do through the REST API url /_api/social.following/my/followed(types=2), we can see that actually nothing happened: the document is still being followed! Let’s bear in mind that the same code does work for tags and sites, but as shown, not for documents:

Here you haz teh Code

var documentUrl = "https://TENANTNAME.sharepoint.com/Gedeelde%20%20Documenten/demofile.txt";
var followingManagerEndpoint = "https://TENANTNAME.sharepoint.com/_api/social.following";

// Make the current user stop following a document.
// The request body includes a SocialActorInfo object that represents
// the document to stop following.
function <strong>stopFollowDocument</strong>() {
  $.ajax( {
    url: followingManagerEndpoint + "/stopfollowing",
    type: "POST",
    data: JSON.stringify( {
      "actor": {
        "__metadata": {
          "type":"SP.Social.SocialActorInfo"
        },
        "ActorType":1,
        "ContentUri":documentUrl,
        "Id":null
      }
    }),
    headers: {
      "accept":"application/json;odata=verbose",
      "content-type":"application/json;odata=verbose",
      "X-RequestDigest":$("#__REQUESTDIGEST").val()
    },
    success: function () {
      alert('The user has stopped following the document.');
    },
    error: requestFailed
  } );
}
 
function requestFailed(xhr, ajaxOptions, thrownError) {
  alert('Error:\\n' + xhr.status + '\\n' + thrownError + '\\n' + xhr.responseText);
}

// Make the current user start following a document.
// The request body includes a SocialActorInfo object that represents
// the document to follow.
// The success function reads the response from the REST service.
function <strong>followDocument</strong>() {
  $.ajax( {
    url: followingManagerEndpoint + "/follow",
    type: "POST",
    data: JSON.stringify( {
      "actor": {
        "__metadata": {
          "type":"SP.Social.SocialActorInfo"
        },
        "ActorType":1,
        "ContentUri":documentUrl,
        "Id":null
      }
    } ),
    headers: {
      "accept":"application/json;odata=verbose",
      "content-type":"application/json;odata=verbose",
      "X-RequestDigest":$("#__REQUESTDIGEST").val()
    },
    success: function (responseData) {
      stringData = JSON.stringify(responseData);
      jsonObject = JSON.parse(stringData);
      var statusMessage = {
        0 : 'The user has started following the document. ',
        1 : 'The user is already following the document. ',
        2 : 'An internal limit was reached. ',
        3 : 'An internal error occurred. '
      }
      alert(statusMessage[jsonObject.d.Follow] + 'Status code = ' + 
      jsonObject.d.Follow);
    },
    error: requestFailed
  } );
}

 

 

Now for the other way ‘round: when I use the followDocument() call to try to follow the document (whether it was already followed or not), I get a 404 error back through the error function ‘requestFailed’:

followDocument -> Error:\n404\n\n{“error”:{“code”:”3, Microsoft.Office.Server.Social.SPSocialException”,”message”:{“lang”:”en-US”,”value”:”The target of the action was not found. Internal typename: Microsoft.Office.Server.UserProfiles.FollowedContentException. Internal errorcode: 12.”}}}

Isn’t this weird? What’s up Microsoft, are you secretly getting rid of (parts of) this API?

Workaround

Fortunately we can work around this issue by leveraging the SP.Social.SocialFollowingManager in our code instead of the REST API:


var context = SP.ClientContext.get_current();
var socialManager = new SP.Social.SocialFollowingManager(context);
var socialObject = new SP.Social.SocialActorInfo();
socialObject.set_actorType(objectType);
var result = socialManager.follow(socialObject);
context.executeQueryAsync(…);

 

Conclusion

It seems wise to start refactoring your ‘social’ code for SharePoint Online to use the SP.Social.SocialFollowingManager instead of using ajax REST API calls to the /_api/social.following endpoint. Still I haven’t seen an official statement of Microsoft saying that this feature will be discontinued or whether this might be a bug. Tech support of MS wasn’t able to give me an answer to this question.

Upgrading SPFx 1.0 React WebParts

When the SharePoint Framework first came out I spent a good amount of time porting over ‘old’ webparts to the new Modern standard. When someone discovered a bug in one of these SPFx 1.0 apps some time ago it was the perfect occasion to upgrade these to 1.4.1, especially since it is now possible to embed all client side content (images, javascript) inside the app itself, making the process of putting the scripts onto a CDN no longer necessary. What I quickly discovered though is that this isn’t as easy as just updating a few packages in NPM. To spare you the trouble of having to find this out for yourself, follow this guide to easily update your webparts.

Step 0: How not to update

At first I thought that updating would be as simple as just using NPM to update each individual package. This turned out to be the wrong choice, since package updates are not the only thing that has changed since the first release of SPFx and it doesn’t always necessarily use the latest version of 3rd party packages.

There’s a (relatively) easy way though; let the Yeoman scaffold tell you which packages to use. The steps below will guide you through the process of getting the latest generator and checking which changes you need to make to your existing project.

Step 1: Updating Yeoman and the SharePoint Generator

First of all we’ll update both Yeoman and the SPFx generator. Use the following commands in a command console window:

npm install yo@latest -g
npm install @microsoft/generator-sharepoint@latest -g

Step 2: Generate a new webpart

Navigate the command console to a temp folder and use Yeoman to generate a new app scaffold:

yo @microsoft/sharepoint

When Yeoman asks you what type of component you want to create, choose the same type as the app you’re going to upgrade.

Step 3: Copy the packages.json content

Now that Yeoman has generated a new app scaffold, we can open its package.json file to see exactly which packages/versions are currently required for the latest version of SPFx.

Next, find the package.json file in the project you’re trying to upgrade and make sure that the packages and versions under dependencies and devDependencies match. In my case I just shamelessly copy/pasted the entire file content (make sure you change the name afterwards) but if you added any additional packages which are not part of SPFx by default you might have to be a bit more careful.
Once you’ve updated your file, open a command window to the project you’re updating and use the following commands;

gulp clean

The command above cleans up the SharePoint build folders.

npm install

Next, tell NPM to install your packages, which it will do according to your new packages.json.

gulp build

Now we’ll attempt to build the webpart. You might be disappointed to learn that you’re still getting errors – but this leads us to the next step…

Step 4: Fix errors

Even though you’ve updated all your packages, you’ll probably still be getting plenty of errors in your build. This is the trickiest step, since what you’ll have to do to fix them is heavily dependent on your coding style and the changes that have been in the latest SPFx packages. So the changes that I had to make might not be the same for you, but I’ll at least share some pitfalls I’ve encountered to spare you the same troubles.

React Components

One of the build breaking changes in React is that when extending Components, you can no longer use void as the parameter for the component state. In my original webpart there were a lot of components defined like this:

export class Filter extends React.Component<IFilterProps, void> {

…which should be changed to:

export class Filter extends React.Component<IFilterProps, {}> {
Node_modules Folder

If your code still doesn’t build and you’re pulling out your hair in frustration, there might be some old files lingering in your node_modules folder. Try deleting the node_modules folder for your project and running npm install again.

Step 5: Success!

If your code builds again, congratulations! If not, I hope that I’ve at least managed to get you a few steps ahead. For me, the steps above were enough to update my SPFx 1.0 webpart to 1.4.1, but of course I can’t guarantee that the same will be true for everyone.

© 2019 ROphelders.com

Theme by Anders NorénUp ↑