Cancel Running workflows prior to saving an item.

This is a problem for workflows that delay until a certain date before sending out reminders. The user creates an item with 1/27/2018 as the due date and the workflow starts waiting until 1/27/2018 to send out a reminder. If the user then edits the item and changes the date to 1/27/2018, no new workflow is

Started, and the running workflow is unaware the date has changed.

This is typically resolved by  setting up a workflow to run as part of a retention policy. This can sometimes be troublesome if you want to send out multiple reminders –say  7 days before due and 3 days before due—because you need to set up calculated fields with the reminder dates.

But there is another way.  With a bit of JSOM code, a sharepoint edit form can be made to cancel

the running workflow prior to saving the item to the list. This way a new workflow gets started when you save the item (provided you have the workflow configured to run when and item is added AND when an item is changed.

If you are using an SPFX webpart as your edit form (which is unfortunately not currently supported on modern lists), the following code can be used to cancel the running workflow just prior to saving the new version.

 private async getWorkFlowDefinitionByName(workflowDeploymentService: SP.WorkflowServices.WorkflowDeploymentService, workFlowName: string): Promise {
    let context = workflowDeploymentService.get_context();
    let wfDefinitions = workflowDeploymentService.enumerateDefinitions(true);
    await new Promise((resolve, reject) => {
      context.executeQueryAsync((x) => {
      }, (error) => {
        console.error("an error occured getting workflow definitions");
    let foundDefinition: SP.WorkflowServices.WorkflowDefinition;
    let defEnum = wfDefinitions.getEnumerator();
    while (defEnum.moveNext()) {
      const wfDefinition = defEnum.get_current();
      if (wfDefinition.get_displayName() === workFlowName) {
        foundDefinition = wfDefinition;
    return Promise.resolve(foundDefinition);
  private async getWorkFlowSubscriptionByDefinitionIdListId(workflowSubscriptionService: SP.WorkflowServices.WorkflowSubscriptionService, workFlowDefinitionId: string, listId): Promise {
    let context: SP.ClientRuntimeContext = workflowSubscriptionService.get_context();
    let wfSubscriptions: SP.WorkflowServices.WorkflowSubscriptionCollection =
    await new Promise((resolve, reject) => {
      context.executeQueryAsync((x) => {
      }, (error) => {
        console.error("an error occured gettin workflow subscriptions");
    if (!wfSubscriptions) {
      alert("Failed to load workflow subscriptsion. Running workflows were not cancelled. This can happen if the Office 365 workflow service is unavailable.");
      console.error("Failed to load Workflow instances.");
      return Promise.reject("Failed to load Workflow instances.");
    let foundSubscription: SP.WorkflowServices.WorkflowSubscription;
    let subscriptionEnum = wfSubscriptions.getEnumerator();
    while (subscriptionEnum.moveNext()) {
      const wfSubscription: SP.WorkflowServices.WorkflowSubscription = subscriptionEnum.get_current();
      if (wfSubscription.get_definitionId().toString().toUpperCase() === workFlowDefinitionId.toString().toUpperCase()) {
        foundSubscription = wfSubscription;
    return Promise.resolve(foundSubscription);
  private async cancelRunningWorkflows(ItemId: number, listId: string, workflowName: string): Promise {
    if (!workflowName) {
      return Promise.resolve();
    var context = SP.ClientContext.get_current();
    // get all the workflow service managers
    var workflowServicesManager: SP.WorkflowServices.WorkflowServicesManager = SP.WorkflowServices.WorkflowServicesManager.newObject(context, context.get_web());
    var workflowInstanceService: SP.WorkflowServices.WorkflowInstanceService = workflowServicesManager.getWorkflowInstanceService();
    var workflowSubscriptionService: SP.WorkflowServices.WorkflowSubscriptionService = workflowServicesManager.getWorkflowSubscriptionService();
    var workflowDeploymentService: SP.WorkflowServices.WorkflowDeploymentService = workflowServicesManager.getWorkflowDeploymentService();
    //Get all the definitions from the Deployment Service, or get a specific definition using the GetDefinition method.
    let wfDefinition: SP.WorkflowServices.WorkflowDefinition = (await this.getWorkFlowDefinitionByName(workflowDeploymentService, workflowName));
    if (!wfDefinition) {
      console.error("Coold not find workflow Definition for workflow named : " + workflowName);
      alert("Coold not find workflow Definition for workflow named : " + workflowName);
      return Promise.resolve();
    let wfDefinitionId: string = wfDefinition.get_id();
    // get the subscription for the list
    let wfSubscription: SP.WorkflowServices.WorkflowSubscription =
      await this.getWorkFlowSubscriptionByDefinitionIdListId(workflowSubscriptionService, wfDefinitionId, listId);
    if (!wfSubscription) {
      console.error("Could not find a subscription for  workflow named : " + workflowName + " ib the TR List");
      alert("Could not find a subscription for  workflow named : " + workflowName + " ib the TR List");
      return Promise.resolve();
    let wfSubscriptionId: string = wfSubscription.get_id().toString().toUpperCase();
    let wfInstances: SP.WorkflowServices.WorkflowInstanceCollection = workflowInstanceService.enumerateInstancesForListItem(listId, ItemId);
    await new Promise((resolve, reject) => {
      context.executeQueryAsync((x) => {
      }, (error) => {
    if (!wfInstances) {
      alert("Failed to load workflow instances. Running workflows were not cancelled. This can happen if the Office 365 workflow service is unavailable.");
      console.error("Failed to load Workflow instances.");
      return Promise.resolve();
    var instancesEnum = wfInstances.getEnumerator();
    let runningInstance;
    while (instancesEnum.moveNext()) {
      var instance = instancesEnum.get_current();
      let instanceSubscriptionId = instance.get_workflowSubscriptionId().toString();
      let instanceStatus = instance.get_status();
      if (instanceSubscriptionId.toUpperCase() === wfSubscriptionId && instanceStatus === 1) {
        runningInstance = instance;
    if (runningInstance) {
      await new Promise((resolve, reject) => {
        context.executeQueryAsync((x) => {
          console.log("Workflow Termination Successful");
        }, (error) => {
          console.error("Failed to terminate workflow.");

With the above methods in place , you just need to call

if (originalReuiredDate != tr.RequiredDate) {
await this.cancelRunningWorkflows(itemId, listId, workflowName).then((x) => {
console.log("Workflow has been terminated");

prior to saving your list item. If there is an instance of the workflow already running , it will be canceled and a new workflow will start once your item is saved.

Posted in react, spfx, Uncategorized | Tagged , | Leave a comment

Code Editor Property Pane Control for SPFX WebParts

I submitted a PR to the spfx-property-controls repository today for a new SPFX property pane control – the PropertyFieldCodeEditor control.
The new control uses the Ace editor under the hood (see
The Ace editor supports editing many types of content with auto-complete, error checking, syntax highlighting , etc.

The PropertyFieldCodeEditor property pane control allows you to edit Json, Javascript, Sass, Typescript, Plain text, HTML, Handlebars and XML code within a language-aware editor, right from the property pane.

I can be use to :
• edit the XML needed to add an SPFX webpart to a page (that’s the reason it was created originally)
• edit HTML snippets to be shown in an SPFX webpart
• edit plain text to be shown in an spfx webpart

• edit a CAML query to be passed to renderListDataAsStream
• edit JSON values to pass complex data structures to a webpart
• edit Handlebars templates to be used in an spfx webpart
• edit javascript snippets -?
• Edit typescript code _? (we could have an azure job compile!)
• and more…

Merry Christmas!



Posted in react, sharepoint, spfx, Uncategorized | Tagged , | Leave a comment

Using Async/Await with JSOM

Async/Await can make JSOM coding much easier and is simple to set up. All you need to do is wrap your executeQuery calls in a Promise, then you can await them!

await new Promise((resolve, reject) => {

clientContext.executeQueryAsync((x) => {


}, (error) => {





Here’s a full example of a method that hides the firs webpart on the page:

public async AddWebPartToEditForm(webRelativeUrl: string, editformUrl) {

const clientContext: SP.ClientContext = new SP.ClientContext(webRelativeUrl);

var oFile = clientContext.get_web().getFileByServerRelativeUrl(editformUrl);

var limitedWebPartManager = oFile.getLimitedWebPartManager(SP.WebParts.PersonalizationScope.shared);

let webparts = limitedWebPartManager.get_webParts();

clientContext.load(webparts, 'Include(WebPart)');


await new Promise((resolve, reject) => {

clientContext.executeQueryAsync((x) => {


}, (error) => {





let originalWebPartDef = webparts.get_item(0);

let originalWebPart = originalWebPartDef.get_webPart();



await new Promise((resolve, reject) => {

clientContext.executeQueryAsync((x) => {

console.log("the webpart was hidden");


}, (error) => {







Posted in Uncategorized | Leave a comment

Displaying Rotated Column Headers on an Office UI Fabric Details List within an SPFX Webpart

Sometimes you need to display a grid where the data displayed within the grid’s columns is much narrower than the column headers. For instance you may need to show a user’s name in the column header and just a checkbox in the details indicating that the user has some attribute. In such cases it is often useful to rotate the grids column headers at a 45-degree angle so that more columns can fit on the screen as described here.


The link above shows how this can be accomplished in HTML Tables. The Office UI Fabric DetailsList is, however, rendered using <div> tags (at least for now). The <div> tags used to display the DetailsList each have a css class representing their use—for instance ms-DetailsHeader or ms-DetailsHeader-cell. We cannot set these css classes in our application’s app.module.scss because the css clasnames would be renamed to be specific top our application, and fabric would not recognize those class names as noted here.


Instead we can add a .scss file to our solution in addition to the .module.scss file. The .scss file won’t have its classes renamed as noted in the link above. Then in the .scss file we can enter the following code:

.ms-DetailsHeader { //* when adjusting line height also need to adjust second parameter to transform:translate below

height: 140px;

white-space: nowrap;


//*Set the wrappers around the headers to display at 45degree angles


transform: translate(25px,40px ) //*Change second parameter when adjusting line height


padding-bottom: 90px;

width: 36px !important;





vertical-align: bottom;


.ms-DetailsHeader-cell span {

overflow: visible;


This scss sets up the classes to rotate the column headers of any column defined with

headerClassName: “rotatedColumnHeader”,

minWidth: 20,

maxWidth: 20,

With this configuration the columns only take up 20 pixels, bit the headers are displayed at a 45 degree angle so that they are visible.


In addition to setting the headerClassName of each column, we also need to import our .scss file into our react component with this code, placed in the react component:



Once this has been set up properly the column headers will display as noted in the article above:




A working example can be found here



Posted in office-ui-fabric-react, react, spfx | Tagged , , , , , , , | Leave a comment

Using the Office UI Fabric Grouped DetailsList in React-based SPFX Webparts.

Say you have an array of objects in your application that looks like this

export class Widget {

public constructor(

public id: number,

public title: string,

public isActive:string,

public manufacturer:string

) { }


And you want to display the items in a react DetailsList  showing the ‘title’ of each item grouped by manufacturer.


The JSX to render such a display could be set up like this:








{ key: “title”, name: “Widget Name”, fieldName: “title”, minWidth: 20, maxWidth: 100 },





The getAvailableWidgets is defined as

public getAvailableWidgets(): Array<Widget> {

var tempWidgets = _.filter(this.props.widgets, (widget: Widget) => {

return !this.trContainsWidget(,;


var widgets =, (widget) => {

return {

title: widget.title,

manufacturer: (widget.manufacturer)?widget.manufacturer:”(none)”,




}).filter((p)=>{return p.isActive===”Yes”});

return _.orderBy(this.state,widgets, [“manufacturer”], [“asc”]);



The key point is that the list returned must be sorted by whatever fields you want to group on.



The getAvailableWidgetGroups is defined like this:

public getAvailableWidgetGroups(): Array<IGroup> {

var pigs: Array<Widget> = this.getAvailableWidgets();

var widgetManufactureres = _.countBy(pigs, (p1: Widget) => { return p1.manufacturer; });

var groups: Array<IGroup> = [];

for (const pm in widgetManufactureres) {


name: pm,

key: pm,

startIndex: _.findIndex(pigs, (pig) => { return pig.manufacturer === pm; }),

count: widgetManufactureres[pm],

isCollapsed: true



return groups;



The method returns an array of IGroup which is required to group a Detailslist. The IGroup needs the starting row number of each group and the number of element in the group. The method uses lodash to get these values.


The lodash countBy function returns an object whose keys are the distinct names of the manufacturers and whose values are the number of rows with that manufacturer.

So to create the array of IGroup, it just loops through each manufacturer and creates an IGroup. It uses the findIndex  function to find the first row with the manufacturer and gets the count from the widgetManufactureres object.


Posted in fabric, react, spfx, Uncategorized | Tagged , , | Leave a comment

Debugging SPFX Gulp build tasks in VS Code

In VS Code click View==>Debug from the main menu

The Debug box will appear:


Click the dropdown in the Debug Box and select Add Configuration.

From the list of configurations select {} Node.js: Launch Program:



Create a configuration with the following settings:


“type”: “node”,

“request”: “launch”,

“stopOnEntry”: true,

“name”: “node-gulp”,

“args”: [




“program”: “${workspaceRoot}\\node_modules\\gulp\\bin\\gulp.js”,

“cwd”: “${workspaceRoot}”,

“runtimeExecutable”: null,

“env”: {




In the configuration I wanted to debug the Bundle –ship command, so I just entered those strings in the args[].


Now, back in the Debug Box, hit the dropdown again and select the configuration you just created (in the example above I called it node-gulp). Then hit the green arrow in the debug box and you will hit a breakpoint on the first line of the gulp.js file:


You can place additional breakpoints in any of the gulp task .js or .ts files that live in node_modules by clicking in the left margin of the editor. A good starting point is node_modules/@microsoft/sp-build-web/lib/SPWebBuildRig.ts.


See the blog post at for more info.

Good luck!


Posted in javascript, react, spfx, Uncategorized | Tagged | Leave a comment

Search-Based Global Navigation in SharePoint On-line

While migrating our Team Sites from SharePoint 2010 to SharePoint On-line we noticed that response time of the sites on SharePoint Online was significantly slower than SharePoint 2010. Researching it we discovered that the issue was caused by our use of structural navigation, and led us to this article about Navigation Options for SharePoint Online.

While the article makes the case that Search based navigation good   because it is security trimmed (as opposed to Managed Navigation), and fast (as opposed to Structural navigation), it states that Search based Navigation can only be implemented with a custom master page. This is incorrect.

I rewrote example script that the article provided to work with standard JavaScript (I removed the need for knockout) and to produce [hopefully] the exact same html that is produced by the out-of-the-box structural navigation provided by SharePoint.   The script is deployed to the SharePoint on-line site using the PnP Add-SPOJavascriptLink, and provides the same navigation experience as Structural Navigation in a fraction of the time.  (In my case response time wen from 5 seconds to under two seconds!)

Because the script produces the same navigation provided by the out of the box structural navigation, it works well with the PNP responsive-UI solution.   The only downside I see is that users cannot reorder the navigation Items,

The Script can be downloaded here and uploaded to a location on your tenant.

To install the script on your site collection run the command:

Add-SPOJavaScriptLink -Name “SearchbasedGlobalNav” -Url “url-of-the-uploaded-script” -Scope Site -Sequence 500


Posted in Powershell, search, sharepoint, Uncategorized | Tagged | Leave a comment