Year2018

Responsive SPFx Controls with Fabric UI React

Have you ever built a control that you want to have behaving differently depending on whether you’re working on a mobile device or a desktop? For example, showing a callout when you’ve got a full resolution desktop screen available but using a screen covering panel on your mobile phone? Well, Fabric UI React continues to be a life saver and makes implementing this almost ridiculously easy with its ResponsiveMode utlility.

On a high resolution screen, the extension renders a Callout

On small screens, the extension uses a full-screen panel

The first thing you need to do is import the necessary interfaces and classes;

import { IWithResponsiveModeState, ResponsiveMode, withResponsiveMode } from 'office-ui-fabric-react/lib/utilities/decorators/withResponsiveMode';

Next, create an extension for your control’s Props interface to also implement IWithResponsiveModeState

export interface INavigationCalloutInternalProps extends INavigationCalloutProps, IWithResponsiveModeState { }

Finally, add the ‘@withResponsiveMode’ decorator to your class and make sure that your control uses the extended Props interface;

@withResponsiveMode
export class NavigationCallout extends React.Component<INavigationCalloutInternalProps, INavigationCalloutState> {

Now we’re all set to make our control change its behavior based on screen size. In our render() function, we can now do the following:

const isSmall = this.props.responsiveMode! &lt;= ResponsiveMode.medium;
{ isSmall ? (
      <Callout></Callout>
   ) : (
      <Panel></Panel>
   )
}

Staying Signed Into Multiple Office365 Tenants with Google Chrome Profiles

As a SharePoint developer working with multiple customer tenants, I find myself logging in and out of different accounts continuously. At best I could use InPrivate windows to be signed into two different tenants at the same time but it’s always been a massive annoyance. Last month though at the European Collaboration Summit (great convention, by the way) I attended a Microsoft Q&A session and the answer to the question “Why do even the speakers from Microsoft use Chrome instead of Edge in their demos?” changed my life. The big secret is that Google Chrome allows you to create user profiles which have their own isolated browser sessions (thanks @vesajuvonen).

Setting it up is as simple as going into the browser settings and clicking on ‘Manage Other People’. You can then start different browser instances for each different tenant you have to log into; you can finally stay logged into your own company’s intranet while also working on a client’s SharePoint sites. These different browser instances also have their own sessions and cookies so if you opt to ‘stay signed in’, you won’t have to log in as often on that profile.

It’s a simple trick but if you’ve ever had to work with multiple different O365 tenants, you know how much of a time saver it can be.

Why can’t I create a new site collection in SP Online ?

This week I encountered a strange feature of the SharePoint Online Admin module. I was trying to create a fresh site collection to migrate an existing site collection to, but for some reason, and without any feedback, the creation process of the new site collection seemed to fail. After trying a few times, I realized this was not working 😉

I decided to use SharePoint Online Management Shell to try and see if I would receive some feedback as to why my site collection wasn’t created. Commands to create a site collection (you have to be a tenant admin to use these):

First connect to the tenant admin module:

Connect-SPOService -URL https://yourtenant-admin.sharepoint.com

Then create the site collection using the following command:

New-SPOSite -Url https://yourtenant.sharepoint.com/sites/sitecollectionname -Owner admin@yourtenant.onmicrosoft.com -StorageQuota 100 -Title "Site collection title" -ResourceQuota 300 -NoWait

There are more switches you can use to create a site collection, like choosing the site template, etc. See the MS docs for this.

To my surprise, I got an error message telling me that the site collection I was trying to create already existed – in the recycle bin of the tenant!

Now I remembered: We had previously done a test run of the migration of the site collection and afterwards deleted it! So it was just a matter of clearing the site from the recycle bin, either through the UI of through the command that PowerShell offered me:

Remove-SPODeletedSite -Identity https://yourtenant.sharepoint.com/sites/sitecollectionname

When trying to create a new site collection now, all went well. Still a bit strange that the SharePoint admin UI didn’t offer me this feedback in the first place.

Happy site collection creation!

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!

Working with SPFx projects in Visual Studio 2017 (or: how to avoid Visual Studio Code)

I like the SharePoint Framework. It’s new, it’s fast and it’s miles ahead of what we’ve had before. I even like Node.js, NPM, Yeoman and the rest of the development ecosystem that comes with it. What I don’t like though, is Microsoft’s insistence that I should use Visual Studio Code. Even the name bothers me – Visual Studio Code – what the hell, Microsoft; what did you think I was using your previous versions of Visual Studio for? Novels? Why can’t I stick to good old Visual Studio 2017 with its plethora of features and extensions?

It’s still possible to keep working in Visual Studio 2017 of course, although you won’t have the comfort of Project Templates to help you along. My solution is as follows:

  • Create an empty Class Library Project
  • Open a command window to the path of the project (not the solution)
  • Run the SPFx Yeoman Generator (yo @microsoft/sharepoint)
  • Keep working in Visual Studio 2017 stubbornly

Note: Visual Studio 2017 has some issues with React so don’t expect to be able to rely on IntelliSense to show you where the errors in your code are. I’ll update this post if I ever find a solution to this, but for the time being I’m just happy to be able to work in the IDE of my choice.

Running the project

Wouldn’t it be nice though if pressing F5 would automatically execute a ‘gulp serve’ command? Well; that’s easy to set up too.

In the Properties of your project, navigate to the Debug tab and set the Launch property to ‘Executable’.

Next, enter ‘cmd’ as your executable.

Finally, set the Application arguments to

/K gulp serve

This will tell Visual Studio to open a command window and execute gulp serve when the project is started.

The Visual Studio Project Template

Wouldn’t it be nice though if there was a pre-made Visual Studio Project template and you didn’t have to  go through all these steps to run your SPFx project? Well, there is – kind of. The SPFx Project Template Extension – which basically runs the steps outlined in this blog post but automated in the form of a project template – is very promising but doesn’t seem to be actively maintained and only works for webparts, not extensions. As such, for the time being I can’t really recommend it, but it’s a good initiative to retain the ease-of-use found in Visual Studio 2017  for SPFx projects.

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 ↑