Nintex Workflow - Lazy Checkin Everything Workflow

Doing quite a bit of client-side JavaScript (AngularJS in fact) and checking in and out lots of files that sit within Style Library.  At the end of the day, I just want something that will go through everything and check them all in for me.

Nintex Workflow time

  • Query List of items still checked out to me
  • Loop through list
  • Check-in
  • Bonus: Run-As-John

Query List

Create a Site Workflow, add a Query List action then set the filter.  Collect "URL Path" and "Checked Out To" fields to separate collection variables.

Important: Make sure you tick Recursive

Hit Run Now.
I want to change this part of the Query to Current User.

<Eq>
<FieldRef Name="CheckoutUser" />
<Value Type="Integer"><UserID Type="Integer" /></Value>
</Eq>

Hit the Execute button - looking good. 

Return to Configure Action.  Make the same change to the CAML query.

Loop and Check-In

Wrap up the rest of the Workflow.  After the Query List, set up a For Each action and loop through each URL in the URL Collection.

For each URL, run the Check in item Action.  Set it to check in by URL.

Insert Witty Comment.  Log an entry to History list for testing.

Laziness Complete

And done.  Workflow runs, John's files are checked in.

Bonus: Run-As-John

Sometimes, you might want to let a colleague check in your files on your behalf.  Nintex let you set up workflow within an Action Set to run as the Workflow Owner.

Summary

  • Now Everyone can check in all John's Mess!
  • Put this on a weekly schedule.

 

A Hybrid Future for On-Premises

Hybrid - In Theory

I think it is no suprise for us watching from the SharePoint world (sometimes with a slight envy) at all the investments in the cloud.

Microsoft makes no secret about this - cloud is a massive growth area and an area that Microsoft is and will aggressively pursue.

SharePoint itself is a product born On-Premise. But many of the Experiences are now born-in-the-Cloud.

What I was very relieved to see though, is that in this mad push for Cloud-First, Microsoft reaffirms that they will not leave their customers behind. This is where I feel the Hybrid story that has came out is so refreshing.

What's coming down?

  • OneDrive for Business coming to SharePoint 2010
  • Delve coming to SharePoint 2013 first
  • Continue to evolve Hybrid Search

Hybrid in the Real World

The landscape "I" see.  This part is where I get yelled at, or perhaps I'm seen as a Fanboy.  I'll just say what I saw.

In the year 2013 - I saw the future that Microsoft wanted was all Cloud.  I was very dismayed - Australia is not particularly fast at going to the cloud.  Many of our enterprises aren't even migrating their SharePoint installations from 2010.  What about data sovereignty?  In the light of NSA spying case in 2014 it looked even worse. 

In the following year June 2014, the Australian Government modified its policy to say it is up to each Department Head to decide whether it is OK to store data offshore.  No doubt pushed by both budget cuts, internal push, external Vendor Pricing and a public statement of cutting out unnecessary Red-Tape.

Now, I hear cloud being implemented left and right.  Prime examples?

  • Exchange Online - much bigger mailboxes than on-prem.  Mobile friendly.
  • To get Exchange Online, a company pretty much has their Active Directory synchronized to Azure AD.  ADFS is nicer for SSO, but more servers.  Small and medium enterprises are pretty happy with DirSync.  That's another tick.
  • OneDrive for Business - relatively large personal storage space that allows Enterprise IT control
  • Office Client Licenses.  As part of the Office 365 package, the cheaper client licenses (and up to 5 devices, as well as additional mobile/tablet licenses) are also a huge win.
  • Yammer - Corporate-friendly, sanctioned "social platform".  Seriously, your youngster employees wants to talk, at least give them the right place to make that conversation heard.
  • Lync/Skype for Business - Lync Online took care of a lot of remote VOIP scenarios.  Lync Server worked well with Polycom and other On-Prem solutions.
  • Extranet Sites (SharePoint) where the company wants to share "some" content with an external partner.

There will always be companies that can't move everything to the cloud, but I think more and more companies are considering what they *could* move.  Most companies don't really want to host their own Exchange Server, unless they really have to.  And even for those rare cases, my Bank client is implementing Yammer as their Enterprise Social solution.

 

Reading the Tea Leaves

Oh my favourite activity.  I love doing this and yet I'm so bad at it.

I'm terrible at reading the future.  So I only wanted to mostly comment on the past.  Perhaps as a consultant that works across many different sectors (building, education, transport, mining and banking), and as a community person that loves to talk to everybody I meet, I do see quite a bit.

And what I see aligns with what Microsoft is doing.  So I think it's safe to predict this one:

Bet on the Cloud.  And if you can't do that yet, Bet on Hybrid.

 

SharePoint Add-in: Accessing Webcam with Only Javascript

This blog post details how to access your Webcam via Javascript through the browser, and upload that content to a SharePoint library.  Then, with an added bonus, set it to be your User Profile Picture.

I have build this User Profile Webcam solution as an Add-in for SharePoint (was App for SharePoint).  This is a SharePoint Hosted app.  All the code runs in the browser and access SharePoint via the SharePoint Online API.

Step 1.  Access your webcam.

Modern Browsers:  Immersive Internet Explorer, Firefox, Chrome and Safari all has ways to access your webcam via

getUserMedia

 

But this doesn't work on IE (Desktop).  So for that we'll add a Polyfill (Flash). 

 

There's a project on github that does this nicely, and I use large chunks of the code from their demo.

https://github.com/addyosmani/getUserMedia.js

<script type="text/javascript" src="../Scripts/html5.js"></script>
<script type="text/javascript" src="../Scripts/getUserMedia.min.js"></script>
<!-- Add your JavaScript to the following file -->
<script type="text/javascript" src="../Scripts/App.js"></script>


<div id="webcam"></div>
<canvas id="canvas" height="240" width="320"></canvas>

<br />
<button class="btn" style="width:140px;" id="takeSnapshot">Take a picture</button>
this.snapshotBtn = document.getElementById('takeSnapshot');
$(this.snapshotBtn).click(this.getSnapshot);

this.getSnapshot = function () {
    // If the current context is WebRTC/getUserMedia (something
    // passed back from the shim to avoid doing further feature
    // detection), we handle getting video/images for our canvas 
    // from our HTML5 <video> element.
    if (App.options.context === 'webrtc') {
        var video = document.getElementsByTagName('video')[0];
        //App.canvas.width = video.videoWidth;
        //App.canvas.height = video.videoHeight;
        App.canvas.getContext('2d').drawImage(video, 0, 0, App.canvas.width, App.canvas.height);

        // Otherwise, if the context is Flash, we ask the shim to
        // directly call window.webcam, where our shim is located
        // and ask it to capture for us.
    } else if (App.options.context === 'flash') {
        window.webcam.capture();
        App.changeFilter();
    }
    else {
        alert('No context was supplied to getSnapshot()');
        return false;
    }
}

The code basically takes either the webrtc object and copy the image to the App canvas.  Or use the flash polyfill and Flash will write the image to the canvas element.

 

Step 2. Binary Format

This part is hairy.  But you are a developer, and dealing with Binary Encoding in JavaScript is what we do.

/*
Now, get canvas and do decoding.
1. canvas can return image data in png, in dataURL format.
2. strip the heading string "data:image/png;base64,"
*/

var ctx = App.canvas.getContext('2d');
var imageData = ctx.getImageData(0, 0, App.canvas.width, App.canvas.height);
var dataURL = App.canvas.toDataURL('image/png');
var base64 = dataURL.replace(/^data:image\/png;base64,/, "");

This is base64 dataUrl format.

 

/*
Reading.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Solution_.232_.E2.80.93_rewriting_atob%28%29_and_btoa%28%29_using_TypedArrays_and_UTF-8

We need to convert DataURL to ByteArray
Mozilla has these two functions.
*/

function b64ToUint6(nChr) {
    return nChr > 64 && nChr < 91 ?
        nChr - 65
      : nChr > 96 && nChr < 123 ?
        nChr - 71
      : nChr > 47 && nChr < 58 ?
        nChr + 4
      : nChr === 43 ?
        62
      : nChr === 47 ?
        63
      :
        0;
}

function base64DecToArr(sBase64, nBlocksSize) {
    var
      sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
      nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);

    for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
        nMod4 = nInIdx & 3;
        nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4);
        if (nMod4 === 3 || nInLen - nInIdx === 1) {
            for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
                taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
            }
            nUint24 = 0;
        }
    }
    return taBytes;
}

var byteArray = base64DecToArr(base64);

This gives us ByteArray.  We're getting close.

I'd like to tell you that I spend a good 3 evenings working to this point.  Try debugging that code and see if your eyes bleed!

/*

Finally, 

The ByteArray needs to be encoded as a string, and in the POST call, "binaryStringRequestBody" needs to be "true"

https://msdn.microsoft.com/en-us/library/office/dn769086.aspx
http://blogs.msdn.com/b/uksharepoint/archive/2013/04/20/uploading-files-using-the-rest-api-and-client-side-techniques.aspx
http://sharepoint.stackexchange.com/questions/54218/sharepoint-2013-rest-api-upload-image

*/

var binaryString = '';
var len = byteArray.byteLength;
for (var i = 0; i < len; i++) {
    binaryString += String.fromCharCode(byteArray[i])
}

SharePoint API wants data in BinaryString format.  Which is basically each byte of the ByteArray encoded as a concatenated string.

 

 

Step 3.  Upload to SharePoint

/*
Use RequestExecutor to post file back to sharepoint
*/

var appWebUrl = context.get_url();
var requestExecutor = new SP.RequestExecutor(appWebUrl);

var uploadPictureEndPoint = appWebUrl + "/_api/web/lists/getByTitle(@TargetLibrary)/RootFolder/Files/add(url=@TargetFileName,overwrite='true')?" +
    "&@TargetLibrary='" + "Pictures" + "'" +
    "&@TargetFileName='" + _spPageContextInfo.userLoginName + ".png" + "'";

/*
Using RequestExecutor, don't need REQUESTDIGEST
*/
//var digest = $("#__REQUESTDIGEST").val();

requestExecutor.executeAsync({
    url: uploadPictureEndPoint,
    method: "POST",
    headers: {
        "Accept": "application/json;odata=verbose"
    },
    contentType: "application/json;odata=verbose",
    binaryStringRequestBody: true,  // binaryStringRequestBody must be true
    body: binaryString,
    success: function (x, y, z) {
        alert("Success! Your file was uploaded to SharePoint.");
    },
    error: function (x, y, z) {
        alert("Oooooops... it looks like something went wrong uploading your file.");
    }
});

 

Step 4.  Setting User Profile Picture.

 

var appWebUrl = context.get_url();
var requestExecutor = new SP.RequestExecutor(appWebUrl);
var setPictureEndpoint = appWebUrl + "/_api/sp.userprofiles.peoplemanager/setmyprofilepicture";

requestExecutor.executeAsync({
    url: setPictureEndpoint,
    method: "POST",
    headers: {
        "Accept": "application/json;odata=verbose"
    },
    contentType: "application/json;odata=verbose",
    binaryStringRequestBody: true,
    body: App.binary,
    success: function (data) {
        alert('Set My Profile Picture succeeded, it will take a few seconds for the change to be propagated.');
    },
    error: function (error) {
        alert("Oooooops... it looks like something went wrong updating your profile picture (no permission?).");
    }
});

For this API call to /setmyprofilepicture to succeed, your App must have additional permissions.

This will ask the user (or Tenant Administrator) to grant the correct permission.

If you don't grant this permission, the webcam can still save picture to library, but it won't be able to set picture as user profile picture.

Summary

  • Get Webcam via Browser
  • Process canvas data to imageUrl to byteArray to BinaryString
  • Upload to SharePoint Library
  • Set as User Profile Picture
  • Achieve Zoolander face!
  • You can download the App for free from the Office Store to see it in action.  
    Feel free as a developer to hit F12 and step through the code.
  • Leave a comment below and let me know what you think.  For example, I think there's a need for an Outlook Add-in that uses the webcam.  Especially, if it can run on an iPad.

 

Azure Logic Apps: Build SharePoint Workflows by clicking buttons: a picture guide

 

TOC: Azure Logic Apps

  • Build SharePoint Workflows by clicking buttons [This article]
    • Introduction
    • SharePoint Online
    • Office 365
    • Connect them all
  • Hybrid Workflows - SharePoint On-Premise
  • "Code", Template Language Expression
  • Observations
  • Social
  • XML

 

Introduction - What are Azure Logic Apps

 

Microsoft announced a series of Azure App Services today:

http://weblogs.asp.net/scottgu/announcing-the-new-azure-app-service

 

Specifically, I want to focus on Azure Logic Apps.

http://azure.microsoft.com/en-us/documentation/articles/app-service-logic-what-are-logic-apps/

In Microsoft's words.

Azure App Service is a fully managed Platform as a Service (PaaS) offering ... allow any technical user or developer to automate business process execution via an easy to use visual designer.

Best of all, Logic Apps can be combined with API apps and Connectors from our Marketplace to help solve even tricky integration scenarios with ease.

 

Microsoft has a tutorial on how to create Azure Logic Apps:

http://azure.microsoft.com/en-us/documentation/articles/app-service-logic-create-a-logic-app/

http://channel9.msdn.com/Shows/Azure-Friday/Azure-App-Service-Logic-Apps-with-Josh-Twist

You should go through these first.  There are a number of new Azure templates that are wordy to describe, but a video will show how it all works together fairly quickly.

So I only want to focus on the SharePoint Online Connectors.  They are easy to set up, but actually, tricky to find.

 

Create the Logic App

 

The Workflow:

  • Grab tweets from my twitter timeline and put them into my SharePoint Online List, then email me.

 

Once you watched the video and we start by creating our own Azure Logic App. 

image image

Once it's ready, head into Triggers and Actions - this is where the rules are defined.

image

On the right hand side you wouldn't have any API Apps in the resource group.  We'll configure them in a minute.  Click Visit the Marketplace

 

Configure the Office 365 Connector

 

image

There is a giant header at the top to add Office 365 Connector.  We should go ahead and add that. 

Note, in your happiness to add the Office 365 connector, you will, like me, completely fail to read "send and receive emails, calendar and contacts".  No files, or SharePoint sites.  Essentially, this connector is only for Exchange-related services.

Still useful for sending emails, so let's configure it.

image image image

 

Go back to Triggers and Actions on the Logic App

image

  • Sometimes - the API Apps are listed by their template name and not the API App Name that you've assigned.  This is actually very confusing, I'm sure it'll be fixed soon.
  • Adding either the Office 365 Connector or the SharePoint Online Connector will also add the HTTP and Recurrence API Apps automatically.

 

 

Configure the SharePoint Online Connector

http://azure.microsoft.com/en-us/documentation/articles/app-service-logic-connector-sharepoint/

So far so good.  Let's do the next one.  And I think you'll fall into another hole.

You look at this and you say, surely.  That one is for SharePoint, since the earlier Office 365 connector isn't.

image

 

image

Yes, I say.  That's exactly what I want.  Those are the right triggers and actions!

image

And here is the next hole I fell in.

Chances are, you got all the way to the end and thought why do I need a service bus for Azure Logic App to talk to Office 365.  Something don't smell right.

I have failed to read the text again - this one is for On-Premises SharePoint, and you will need to install a listener proxy that will talk to Azure Logic Apps via an Azure Service Bus.

We'll cover that later when we talk about Hybrids.

For now, go back to the Marketplace.

image

Use the search filter.  There are 2 SharePoint connectors.  The SharePoint Online Connector isn't shown in the default view.

 

image

Create -> Package Settings -> OK <- Create

 

TIP: Unpin

 

image

While you wait for the API App to be created, your Startboard is currently looking like a mess.  Right click on the other connectors and unpin from Startboard.

 

Add the Twitter Connector

This is in the video above.  So I'll jump through this one really quick.

image

More spinning.  All done!  OK now we have all our connectors.  Let's look at them.

 

Triggers

 

I want to show the various different triggers from different connectors.  But I won't actually use these in the later example.

image image image image
Office 365 Trigger looks like this. SharePoint Online Trigger looks like this Twitter Trigger looks like this The trigger I want to use is a simple Recurrence timer.  It will run every hour.

 

Activities

 

image

Next, pull down some tweets.

Add Twitter Connector and Authorize

image image

Twitter Authorize, and set up to grab my timeline.

I want to put them into SharePoint Online.

image image

Click Authorize - you'll see a pop up

  • I want this to talk to my Office 365 Work Account - so pick the top one. 
  • Note - sometimes, it doesn't seem to work, I would close the pop up and try Authorize again.  I consider this another Preview Bug

image image

You should see these actions to perform on the connector.  Notice it connects to two lists specified during the setup, and there doesn't seem to be a way to change that afterwards.

Pick Insert Into tasks

 

We'll need to loop through the tweets and insert each one.

image  imageimage

We need to change the TweetText reference from the First tweet to Each Repeating Item tweet.

Change it from:

  • @first(body('twitterconnector')).TweetText

to

  • @repeatItem().TweetText

 

 

Last action is an email.  Add the Connector.

image image

The Office365 Connector (Exchange) is the same as Office 365.

I set the body to the created date of the first tweet (in descending order, so actually the latest tweet).

  • @first(body('twitterconnector')).Created_at

 

The big picture:

image

 

Save

image

:-)

You save and close the Triggers and Actions editor.

image

:-(

Thanks.  I think you forgot I had just saved...  (another preview bug)

 

Back on the Logic App screen, I see this:

image image

With a Recurrence trigger I find it always run first time I save.  But if it doesn't, you can manually Run Now

 

And the results

 

Here we have it.

The list, in my Tasks list on my SharePoint Online.

image

 

Email

 

The email I received.

image

  • I don't know why the email is default to send with low importance.  You can change this.

 

image image

Click the Pencil - I find that it is not intuitive that's clickable.  I think it should be next to the cog wheel.

 

And that's quite possibly the easiest Tweets to SharePoint Online List example (including set up the infrastructure) that I have ever done.

Summary

 

  • Created Office 365, SharePoint Online, Twitter connectors.
  • Created Azure Logic App on recurrence schedule
  • Write tweets to SharePoint List
  • Next episode, we'll look at Hybrid.  Going Cloud to/from On-Premises.