Enabling LastPass bookmarklet with Microsoft Edge

One of the problems of Microsoft's Edge browser not supporting Extensions right now is that password management with tools like LastPass can be quite difficult.

Here is a workaround with LastPass via Bookmarklets

You need the help of another browser - either IE or Chrome. 
Because Edge doesn't currently have a way to create/modify bookmarklets

1. In IE/Chrome.  Login to your LastPass https://lastpass.com/index.php?ac=1

2. Browser down Tools > Bookmarklet

 

 3. In IE/Chrome.  Drag LastPass Fill! Into the Favourites Bar (IE) or Bookmark Bar (Chrome)
 4. In Edge, go to Settings > Show the favorites bar > On
 5. Import from another browser - choose either IE or Chrome
 6. In Edge.  Once import is complete, drag the bookmarklet into the Favourite Bar.  I also deleted other favourites that I didn't need from the import.

7. You now have a Lastpass bookmarklet!

 

Summary

This works for Twitter and Reddit

I also hope as Favourites gets synchronized across Universal Windows Devices - this should work on Xbox One and Windows Mobile 10 as well.

 

 

 

 

Thinking with JS Promise and Promises

 

Here's a real life example of a quick design iteration that we went through with promises this week.

In AngularJS (but this applies to any JavaScript), we have a function that calls the server to do a long running process.

function longProcess() {
    // dataservice returns a promise and we return that to our caller.
    return dataservice.longservice();
}

As this could potentially take nearly a minute, we want to add a waiting dialog.

AngularJS UI provides a modal dialog service $modal

function longProcess() {
  // create dialog
  var dialog = $modal.open({ templateUrl: 'wait.html' });

  var promise = dataservice.longservice();

  promise.then(
    function(){
      // success
      dialog.close();
    },
    function() {
      // fail
      dialog.close();
    }
  );
  return promise;
}

 

This works great.
But sometimes, the dialog flashes past too fast!  While the user is still trying to read it, it disappears.

Before we run off into code, let's stop a do a bit of thinking.

What we need is to combine the original promise, with a second new timer promise (of say 5seconds).  When both the service call has finished and the timer is up, we will resolve the promises.

So in pseudo code, it'd look like:

var p1 = service();
var p2 = $q.timer(5000);
var all = $q.all([p1,p2]);
all.then( /* resolve */ );

Note: $q is AngularJS' lightweight implementation of a Promise/A library Q.  The pseudo-code assumption was that $q would provide a timer-based promise that detonates once time is up.  In reality, $q doesn't provide such a method.  But the $timeout service in AngularJS returns a promise for exactly this scenario.

 

function longProcess() {
  var dialog = $modal.open({ templateUrl: 'wait.html' });

  var p1 = dataservice.longservice();
  var p2 = $timeout(angular.noop, 5000);
  var arrayOfPromises = [p1, p2];
  var promises = $q.all(arrayOfPromises);
  promises.then(
    function(){
      // success
      dialog.close();
    },
    function() {
      // fail
      dialog.close();
    }
  );

  // returns a combined promise to our caller.
  return promises;
}

 

Summary

The dialog opens and shows wait.html - it closes when the service is complete and when the timer has hit 5 seconds.  

 

 

Racing to the Races - Putting our Office App out there

As I'm posting this blog entry, our (SharePoint Gurus) first Office App (Add-In) would be available on the store.

I might let you in on a secret - it has in fact been in the store in the last few days, but as it is our company's first Add-In, we had some hiccups and had to push out subsequent updates.  We are pleased with this version and we'll run with it to the actual Melbourne Cup race, which actually isn't all that far away.  It would be on November 3, 2015, and the horses list would be available on October 31 - a Saturday, yes that means our Add-In would prompt you to automatically update data on Monday morning.

Feedback through the week from our clients has been very supportive.  This could turn out great (or a great learning experience).  But either way, we have fun and we hope our clients and friends have fun with our App too.

The Team

We are all consultants and this Add-In is something we wanted to build for a long time, but never could tear ourselves away from our great clients to just stop and write this Add-In.

  • We learn AngularJS along the way
  • We became pros at JavaScript Promises... chaining promises, grouping promises, catching error promises and retrying them.
  • Everyone in the company got involved.  We are not a large company, but this one Add-In has 100% contribution from the entire team.
  • We had different people deploying to their own developer sites, both On-Premises and Office 365. 
  • We use TFS but had an open checkout policy (you have to merge any changes).  This turned out not as disastrous as we think, it gave us freedom to work on the project when we can, without having to wait for a certain colleague to check in first.
  • We started the journey a long time ago with Wiki pages, Task lists and Yammer discussion group.  We are now on Office 365 OneNote (available anywhere, offline, and synchronized) and Office 365 Groups for conversation.  We use the Outlook Groups app when we are on the run.  If Office 365 Planner had been available, I'm sure we would be all over it too.  We had a white-board with moving tasks and Post-It notes.

The Stack

The Add-In is a SharePoint-Hosted App. 

Sweepstake Horse says SAAI.  Also, horse is sorry he didn't say on-premises

Sweepstake Horse says SAAI.  Also, horse is sorry he didn't say on-premises


The Learning (so far)

  • v2 will be provider hosted.  The complexity would lie in provisioning, and also not all our consultants are fluent with ASPNET MVC or C#
  • The benefits are to do with ease of updating the various components, and hiding core logic.
  • We may tackle NodeJS instead

You

You should download our Add-In and give it a whirl.  Come next Monday, hopefully we hear good things from you.

We already have people asking to do a Rugby World Cup one next year, which would have been fun, this weekend is finals between Australia vs. New Zealand.

Posting to Office 365 OneNote via PowerShell

This is a guide of links and gotchas of how to post a new page to your OneNote document sitting in Office 365.

Lots of this was learning for me, so thank you:

Background

Todd Klindt asked over the weekend if there's any examples of manipulating OneNote with PowerShell.

Now Todd is a master of PowerShell, which is NOT A DEVELOPER TOOL.  But I did recall OneNote has an ongoing REST API and recently announced they have it working on Office 365 (Preview).

http://blogs.msdn.com/b/onenotedev/archive/2015/04/30/support-for-work-and-school-notebooks-on-office-365-in-preview.aspx

And as a developer I thought I can at least try to write some PowerShell to get this working.  Besides, I want to play with the OAuth stuff and new APIs.

The LEGO pieces are in place, and the cogs in my head are churning.

To talk to Office 365 Unified API you need an Azure Application. 

Register it like this http://blogs.msdn.com/b/onenotedev/archive/2015/04/30/register-your-application-in-azure-ad.aspx

Here's mine:

These are the OneNote permissions you need

 

What you need:

  • ClientID
  • ClientSecret - copy this out if you lose it you'll have to make a new key
  • The Redirect URL

The Redirect URL is an interesting one.  In the case of a web application - this would be your provider hosted web application.  In PowerShell however, we don't actually _need_ a valid web application.  We just need the returned URL which contains the code. 

It has to be https though.  So https://localhost:12345 will work fine.  
In my picture you also see https://dev.office.com that's actually not necessary.  

Onto the PowerShell:

 

#region mini window, made by (Insert credits here)
Function Show-OAuthWindow {
    Add-Type -AssemblyName System.Windows.Forms

    $form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width=440;Height=640}
    $web  = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{Width=420;Height=600;Url=($url -f ($Scope -join "%20")) }

    $DocComp  = {
        $Global:uri = $web.Url.AbsoluteUri        
        if ($Global:uri -match "error=[^&]*|code=[^&]*") {$form.Close() }
    }
    $web.ScriptErrorsSuppressed = $true
    $web.Add_DocumentCompleted($DocComp)
    $form.Controls.Add($web)
    $form.Add_Shown({$form.Activate()})
    $form.ShowDialog() | Out-Null
}
#endregion

Note

This is a brilliant dialog.  WinForms, hosting a web control.  Genius.

 

#login to get an auth code
$clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx="
$redirectUri = "https://localhost:12345"

#$resource = "https://graph.microsoft.com/"
#most examples of Unified API is under graph.microsoft.com, but OneNote is a different resource.
$resource = "https://onenote.com"

#these need to be UrlEncoded so do it first
Add-Type -AssemblyName System.Web

$clientSecret = [System.Web.HttpUtility]::UrlEncode($clientSecret)
$redirectUri = [System.Web.HttpUtility]::UrlEncode($redirectUri)
$resource = [System.Web.HttpUtility]::UrlEncode($resource)

With all the variables we needed, we can begin

#show auth window
$url = "https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&redirect_uri=$redirectUri&client_id=$clientId&resource=$resource"
Show-OAuthWindow

This invokes the WinForm dialog above.

Haha!  WinForm in 2015 - love it!

#grab the auth code
#note the redirect localhost:12345 doesn't need to work - we grab the authcode right off the URL
#then throw away the window

$regex = '(?<=code=)(.*)(?=&)'
$authCode  = ($uri | Select-string -pattern $regex).Matches[0].Value

Write-output "Received an authCode, $authCode"

You have an authCode - this is step 1.  You can now ask Office 365 for Access token and Refresh Tokens.

We don't need to do this from the browser window anymore.  Invoke-RestMethod cmd works great.

#get access code and refresh token
$body = "grant_type=authorization_code&redirect_uri=$redirectUri&client_id=$clientId&client_secret=$clientSecret&code=$authCode&resource=$resource"
$result = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token `
    -Method Post -ContentType "application/x-www-form-urlencoded" `
    -Body $body `
    -ErrorAction STOP

Write-output $result

Well well!  What have we got here.

#grab the access token
$access_token = $result.access_token

$result=Invoke-RestMethod https://www.onenote.com/api/beta/me/notes/pages `
    -Method GET -ContentType "application/json; charset=utf-8" `
    -Headers @{"Authorization" = "Bearer $access_token" } `
    -ErrorAction STOP

Write-output $result

And finally...  Create Page

$body = "<ol><li>Hello</li><li>OneNote</li><li>World</li></ol>"
$result=Invoke-RestMethod https://www.onenote.com/api/beta/me/notes/pages `
    -Method POST `
    -ContentType "text/html" `
    -Headers @{"Authorization" = "Bearer $access_token" } `
    -Body $body `
    -ErrorAction STOP

Write-output $result

Beauty!

Summary of Gotchas

  • On this development environment I didn't have OneNote available in Azure App Permissions.  I found out I need to create a OneNote document in Office 365 first and open it with OneNote online.  That seems to register that I need OneNote API.
  • I then forgot to grant the app permissions for OneNote
  • I didn't UrlEncode the parameters
  • I also had trouble getting Unauthorized when talking to OneNote - because I was requesting the graph.microsoft.com resource instead of onenote.com
  • Sometimes I still get an occasional error in the browser dialog.  Re-running it it disappears. 
  • Also, the point of the refresh token is that you don't need to pop up the user dialog anymore after the first time.  The refresh token should be stored securely and used to request new access-token on subsequent runs.

Had a few head-scratching moments like this.  But it's all great fun.

Download

 

PhantomJS for SharePoint and Office365 at Collab365

Great news everyone!

Collab 365 Global Conference

Collab 365 is a free online conference that's just around the corner - this is run by the team behind last year's successful SP24 conference (which was the craziest 24hours - around the clock and around the world).

https://collab365.conferencehosts.com/SitePages/GlobalConference.aspx

This time, the guys are less crazy and will instead have 12hours spanning over two days.  (So a total of 24 hours).

PhantomJS: Headless Browser for SharePoint and Office 365

I have been having a lot of fun locally presenting this topic in the Office 365 Saturday events around Australia, and will be presenting this online at Collab 365. 

My session is scheduled for 08 October (day 1 - my time), 12PM-1PM.  Please check your local time.  In Australia, this conference spans 08-09 October. 

https://collab365.conferencehosts.com/sitepages/agenda.aspx

PhantomJS is an interesting tool.  It is free - and it is basically browser that you can script to automate many things, without a UI.  I cover scenarios and scripts that will allow you to use it effectively with SharePoint On-Premises and Office 365.

If you are a developer - this is a great tool to add to your toolset.  Even if you aren't - as long as you aren't scared of a bit of scripting, you will still find the scenarios for PhantomJS interesting and, well, different.

There are several other sessions I wanted to catch up on, and plenty of MVPs and Microsoft presenting on all things Office 365. 

I hope to see you at Collab 365!