Friday, December 8, 2017

Better security defaults for Office 365 Groups and document collaboration

image

After working with Office 365 Groups for a while now and helping customers tailor their Office 365 Groups, I have developed a mantra I call Better Defaults. This involves exchanging default settings for more sensible ones – at least in my opinion.

One of the strengths of a SharePoint site is the ability to set very granular permissions. This security strength is also a pain for end-users. Over the years teaching classes and helping customers, getting to grips with AD groups, SharePoint Security groups, permission levels, and all the places you can set them is one of the top three hardest topics. When you tack the simple owner/member permission structure of an Office 365 Group on top of a SharePoint site, it does not help the confusion.

In this post I’ll offer two tips for better defaults regarding content access, one for public groups and one for private groups.

Wednesday, December 6, 2017

Considerations when creating an Office 365 Group using app-only tokens

image

When you create a new Office 365 Group using the Microsoft Graph using app-only tokens, the Group will not have an owner – which is expected as you are creating the group as an application.

After the creation of the group you need to explicitly add an owner, and make sure you also add the same person as a member.

Summarized steps:

  1. Create the group
  2. Add a person as owner
  3. Add the person in 2) as a member

Tuesday, December 5, 2017

A workaround to setting default slicer values in Power BI

There’s a User Voice issue to add an option to set a default slicer value in Power BI, but the feature is not there yet.

There is however a workaround which is not too hard. When you add a slicer to a report you can select values in it, and the selected values will be selected by default when the report is first opened. Next is to hide the slicer control.

First add a slicer to your report, and select the values you want to filter on.

image

Next, add a white rectangle, which you overlay on top of the slicer to hide it.

image

Publish and you’re all set :) You might see a small fade where the slicer is visible for a second, but at least the user cannot change it. To reduce this effect you can reduce the size as much as possible before overlaying with the white box, or move it more off the screen.

Thursday, November 30, 2017

API rant at the end of the day about Office 365 Groups, Yammer and Graph

Sometimes pressure just builds and you need to vent. This is one of those days where I’ll gripe about O365 Groups with Yammer, and about retrieving an Office 365 Group Photo.
image

Self-service of Office 365 Groups with governance and Yammer

In the perfect anarchy world of Microsoft Corp. this is all good. Everyone can create everything from anywhere. I wouldn’t say it really works for Microsoft internally as I’m sure it’s all a big mess with no structure control at all.

The rest of the world like to have some control over the self-service (which I spoke about at ESPC). This is why a typical setup is:
  • Disable Office 365 Groups creation from the default UI for all users except a few in IT
  • Enable a self-service solution which allows approval, configuration, metadata and what not when a user want’s a new Office 36 Group for collaboration.
So far so good. This works just fine.

Then throw in Yammer. If you lock down the oob self-service, your Yammer groups will not get an Office 365 Group – and there are no easy ways to solve this today.
There is also no option to turn of creation of Yammer groups within Yammer itself and roll your own in-place solution.

Which leads to:
  • Users can create all the Yammer groups they want – but if they want them inside some structure we need to teach them to contact IT, so that IT can create the group and apply whatever metadata, configuration and what not.
I work with client which has a multitude of Yammer groups, but from an information architecture point of view they are not all the same. Some are more important than others – and the only way to classify them is to put magic names in the title or description – or if they are connected to an O365 Group we can put matedata in the Graph – but remember – we turned off self-service of Office 365 Groups.

The solution? Allow us to turn off self-service in Yammer, and allow us to create Groups with Yammer using app tokens – preferably via the MS Graph – not via the Yammer API.

Office 365 Group Logos

Fetching the logo data is possible via the Microsoft Graph – but you require a separate call for this. If I create a listing of Office 365 Groups and want to display the logo, I cannot simply add an <img> tag including the Group id and get the image. No, I need to fire off a REST call per image.

The only resource who got this right is Planner using the link:
https://tasks.office.com/tenant.onmicrosoft.com/Groups/GetGroupPhoto?groupId=<id>

..except you need to have logged in to Planner at least once for this URL to just work. If you’re in SharePoint context they have a nice URL:

/sites/groupname/_api/GroupService/GetGroupImage?id='c8ab4e69-a7a1-4f35-b3ba-96616976cd62'&hash=636476452763371703

..except the id in the above URL is not the Group id, but the id of __iconLogo_.jpg in SiteAssets. You need to fire off a REST call to either get the site url and then one to get the logo url – or hardcode to __iconLogo__ for just one extra call.

And then we have Outlook:

blob:https://outlook.office.com/a03b46be-10ad-469e-b5e6-709c34132353

where the id is something I have no idea what is – certainly not the Group id.

Which means dear users – when listing 100’s of Office 365 Groups, you have to live without the logo until I get smart – tough luck!

Or we have this one:
https://outlook.office365.com/EWS/Exchange.asmx/s/GetUserPhoto?email=group@tenant.onmicrosoft.com – which also requires auth and is a REST call, not a direct image link.

Conclusion

Please Microsoft, give us proper and easy ways to create simple solutions! Or I need to go over all existing Groups and set custom metadata for the logo URL, ensuring all data is available on the intial Graph call. See my generic schema post for more info.

Wednesday, November 29, 2017

Retrieving Teams and Yammer endpoints for an Office 365 Group via the Microsoft Graph

image

I’m working on a solution listing all things Groups from Office 365. That be Office 365 Groups with Outlook, Office 365 Groups with Yammer, Office 365 Groups with Teams, or just plain old Yammer Groups standing by themselves. The last one you cannot do via the Graph so let’s just skip that one for now.

I recently wrote about how you can list all O365 Groups which have a Yammer association, but I did not cover how to get the link to Yammer – as I had no clue how. I have now clued myself in, and will share my findings.

Tuesday, November 21, 2017

Fixing DPI Scaling Issues in Skype for Business on Windows 10 and multiple monitors

image

I have a Surface Pro, and when travelling around I often hook up to a random second monitor. The default DPI scaling of the Surface monitor is 175%, while an external monitor in 1920x1200 resolution is set to 100%.

When running Skype for Business on the Surface monitor everything looks ok, but when I move a chat window over to the external screen, it all becomes super big.

I came across a post by Matt WoodWard which pointed me in the right direction but didn’t solve it for me.

I’m on Windows 10 Fall Creators Update (10.0.16299.19), and what did the trick for me was to open up the file C:\Program Files (x86)\Microsoft Office\root\Office16\LYNC.EXE.MANIFEST, and change dpiAwareness from PerMonitor to System. Remember to open the file as an administrator. If not you won’t be able to change it.

After quitting Skype for Business and restarting it, it all worked.

Monday, November 20, 2017

Modifying the query template for the Highlighted Content modern web part – a very brittle solution

image

The highlighted content web part in modern pages is the successor for the content search web part. It does a pretty decent job, but people like me who like to tune what we get back want some more expert settings, much like we had in the content search web part.

In the highlighted content web part you can add multiple filters today, which are then joined with an OR. This is useful for many scenarios, but often you want to have AND to limit results instead. For example all sites with a specific title, and not all sites OR all items with a specific title.

The above scenario is one I just looked at, and I didn’t want to roll my own web part. Elio Struyf has a PnP sample search web part, but it doesn’t support the same visuals – and I’ll probably end up modifying this in the end ;) But that’s not for this post.

I decided to dig into the properties of the highlighted content web part, and turns out it has a property named advancedQueryText, which is the query template. This property is not included in the UI, but using for example PnP PowerShell you can set this.

The below JSON has a couple of modifications. I have added SiteLogo as an extra managed property to use when showing the result preview, and I have added KQL in the advancedQueryText property.

image

You can download the properties file from my GitHub repo and modify as needed: https://github.com/wobba/RandomFiles/blob/master/searchprops.json

The below PnP powershell creates a new page, and adds a web part with the properties specified.

$pageName = "Awesome"
Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/awesome
Add-PnPClientSidePage -Name $pageName
$searchProps = Get-Content -Path .\searchprops.json -Raw
Add-PnPClientSideWebPart -Page $pageName -DefaultWebPartType ContentRollup -WebPartProperties $searchProps

The caveat is of course that if you edit the web part properties in the UI, everything breaks ;) Which means this is not 100% production ready yet. Hopefully the advancedQueryText property will make it’s way to the UI soon.

References:

How to do “starts with” in KQL

image

When I wrote KQL – The basics back in 2014 I forgot to cover how you can achieve starts with for text property queries. It’s not an operator per say, as it combines equals with quotes and wildcard.

To find all items where title starts with Mikael, you can write:

title="mikael*"

Note that title="mik*" will not work, you need to type full terms before the wildcard for this to work, but still quite useful. I have also updated the original post to cover this.

Monday, November 13, 2017

How to list all Office 365 Groups which are Yammer enabled via the Microsoft Graph

image

Office 365 Groups are great, both from a user perspective, and they are also fun to work with as a developer. This time around I wanted to list all Office 365 Groups which have been created via Yammer, as a means to differentiate the type of Office 365 Groups for users.

I started to dig around on properties on the Group object at the /beta endpoint and found the following for a Yammer originating Office 365 Group.

"resourceBehaviorOptions": [
    "YammerProvisioning"
]

Turns out you can use above information in a filter on the /beta/groups endpoint. Using a GET query where you filter on both Unified groups and the YammerProvisioning attribute, will yield the list you are looking for. You can probably omit the groupTypes filter, but it sure doesn’t hurt.

https://graph.microsoft.com/beta/groups?$filter=groupTypes/any(g:g eq 'Unified') and resourceBehaviorOptions/any(r:r+eq+'YammerProvisioning')

There is still no API on the Microsoft Graph to create Yammer enabled Office 365 Groups, but at least we’re one step closer.

Permission wise you need Group.Read.All as either delegated or app only to query for Groups, which means you can easily do this using the GraphHttpClient for SharePoint Framework.

Wednesday, November 8, 2017

Programmatically enable Teams on an existing Office 365 Group via the Microsoft Graph

Microsoft recently released PowerShell commands to work with Microsoft Teams, which is something we’ve been longing for since Teams was released. We now get commandlets to create and manage teams and channels.

PowerShell is nice and all, but if you already have a lot of Office 365 Groups, or have your own provisioning solution for Office 365 Groups, enabling Teams on these groups is something you want to add into your existing process.

image

And turns out it’s quite easy. I fired off Fiddler to see what was happening when executing the PowerShell commands, and lo and behold, we see queries running against the Microsoft Graph. The creation of the group itself happens against the /edu/groups which I’ve never seen before, but that’s not interesting. The next call to create a Team on the group is the important one.

Run an empty payload of {} with a PUT to /beta/groups/<group id>/team and you’re all set. This command will effectively enable Teams on any group (not Yammer ones). Have existing or new Office 365 Groups? Use the above REST command to enable Teams.

I expect the documentation to be updated soon as this is pretty cool stuff, and guess this will move to the v1.0 endpoint in due time. You can read the docs at https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/group_put_team. Also note that app only takes seems not to be supported at this time.

See the complete listing of PowerShell functions to see what is possible in terms of modifying the team and channels. I didn’t have time to trace all the calls – but should be pretty easy to figure it out if needed until docs are updated. For me, enabling of Teams is the most important at the moment.

Tuesday, November 7, 2017

Fetching custom attributes from Azure Active Directory via the Microsoft Graph

This post was inspired by Juan Carlos González who asked a question about retrieving custom/extension attributes from Azure AD via the Microsoft Graph.

image

Custom or extension attributes in on-premises active directory is nothing new, and many have set up synchronizing these to Azure AD as well – which makes sense. Once the attributes are in place, you might want to use them in applications as well, and in todays day and age, using the Microsoft Graph API is the way we play.

Custom attributes are not retrievable directly by their name like for example userPrincipalName.

https://graph.microsoft.com/v1.0/me/?$select=userPrincipalName

Instead they are named with extension_<randomid>_attribute, which means we need to figure out what this random id is. As far as I know you cannot list it via the Graph, but using Azure AD PowerShell it’s doable. The below sample shows a custom attribute named division on my user object.

Connect-AzureAD
$aadUser = Get-AzureADUser -ObjectId me@madcow.dog
$aadUser|select -ExpandProperty ExtensionProperty

Key                                                     Value
---                                                     -----
extension_e96266002973421daef990ab9be89e86_division     64

By looking at the result we have the prefix we need which works just fine in a graph query.

https://graph.microsoft.com/v1.0/me/?$select=userPrincipalName,extension_e96266002973421daef990ab9be89e86_division

image

Sunday, October 22, 2017

Conditional approval in Microsoft Flow

I’m working on self-service solution in Office 365 where a requirement is that your manager has to approve your request. If however you don’t have a manager, then the approval step should be skipped.

image

Flow has an action for retrieving a users manager, so that part is easy. However, if the user does not have a manager, the Flow action will fail. The solution here is to add a parallel branch below the Get manager action. In one path you set the action to run if the previous action failed, in the other if the previous action was successful, like the image below.

image

Friday, October 13, 2017

Using Flow to promote better document sharing and collaboration in your organization

During lunch today I mentioned a customer sending me a document attachment in an e-mail, instead of what clearly should have been a sharing link to the file.

Outlook has excellent capabilities for sending sharing links instead of attachments, and especially internally in organization this should be the preferred approach to avoid e-mail bloat and multiple versions of the truth. Forcing people to change is not easy, but the conversation let to the following Flow urging your colleagues to do the right thing :)

image

Tuesday, October 10, 2017

Deploy a SharePoint Framework application customizer tenant wide

Creating application customizers which are available to all site collections is a very useful feature. Especially when it comes to site provisioning solutions. In my current project I’m creating a top banner which shows if the Office 365 Group is allowed to have external members, or if the team site can be shared with external users. This extension is installed on all new sites during creation.

Want to know more about a custom self-service solution for Office 365 Groups? Join me at ESPC November 16th 2017 where this post will be part of the solution demoed.

image

Wednesday, October 4, 2017

Adding images to SharePoint from a URL - mini life hack

Often you find yourself adding an image to SharePoint which you find on the internet. The upload dialog in SharePoint does not offer to link to a URL outside the domain you are on, so the workaround is to save the image locally, then upload.

At least if you are using Windows there is a quicker approach which usually works, and it also works in most other applications which allow you to upload a file.

Monday, October 2, 2017

Blocking vs. non-blocking Flows

Microsoft Flow has many triggers and one of the most useful ones is the HTTP request trigger, where an external party can call a specified endpoint with a message which will trigger the workflow. Basically this feature has two modes, one where the workflow executes logic and returns the outcome and the other where it does not need to return the outcome.

If you have an application which is translating text and you want the text back to the application you can create a Flow similar to the one below. A request which takes a string as input, the text is then translated to Norwegian, and returned at the end.

image

When running a REST query against the Flow URL using for example PowerShell we get the expected result back.

> $json = '{"thetext":"This is a test"}'
> Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType "application/json"
dette er en test

This above operation is a blocking one. If you however want the translated text to be sent out as an e-mail, you remove the Response action and add an e-mail action.

image

When you now call the Flow URL you will immediately get a 200 success back, while the workflow executes the steps you have added. A non-blocking workflow.

Summary

If you want to use Microsoft Flow as an API in your application with input and output you can include both a Request and Response step. The calling application will then wait until a response is returned (or it times out). If you however want to use Microsoft Flow to execute an fire and forget action, omit the Response step, and the workflow will start executing while your calling application immediately can continue, regardless of what happens in the workflow.

Friday, September 22, 2017

Resolving assemblies when using PnP Core and ADAL / Graph

This is a super technical post and it goes like this:

image

If you use NuGet and reference PnP as well as ADAL and Graph clients as I have above, and you write code to apply a pnp template you might run into DLL hell and get the following exception:

"Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information."

The reason is that PnP take a dependency on both of these libraries, but on older versions. Deep in the code PnP uses reflection to find all serializer types, and it will throw up an assembly loader exception due to you having newer versions of these DLL’s in your project.

You can either align your dependency with what PnP uses, or you add an assembly redirect from the older to newer versions. Below is code which will dynamically resolve to the newest version for these issues.

Note: This assumes your version is newer than whatever PnP takes a dependency on

Add the code in a static constructor, or somewhere else which ensures it runs once for your application.

public static void RedirectAssembly()
{
    var list = AppDomain.CurrentDomain.GetAssemblies().OrderByDescending(a => a.FullName).Select(a => a.FullName).ToList();
    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
    {
        var requestedAssembly = new AssemblyName(args.Name);
        foreach (string asmName in list)
        {
            if (asmName.StartsWith(requestedAssembly.Name + ","))
            {
                return Assembly.Load(asmName);
            }
        }
        return null;
    };
}

Funny part is that it now works fine for me without this code – but, just in case :)

Tuesday, September 19, 2017

Introducing a generic (mostly) all purpose extensibility schema for the Microsoft Graph

If you’ve followed my blog lately you know I’m experimenting with using Graph Extensions from the Microsoft Graph to persist metadata for resources in the graph, more specifically I’m storing metadata about different types of Office 365 Groups.

image

I decided to go with schema extensions over open extensions as this allows me to query and filter on my schema properties, instead of just reading and writing the properties. If you plan on only reading and writing data, I recommend using open extensions – why? Read on!

Wednesday, September 13, 2017

SharePoint Framework helper class for calling the Microsoft Graph

I’m creating a couple of web parts using SharePoint Framework in my current project so I figured I’d share a small helper class I created for performing GET requests against the Microsoft Graph. The code is written in TypeScript and handles single item results as well as paging on array results.

MSGraph.ts

import { GraphHttpClient, GraphHttpClientResponse } from '@microsoft/sp-http';

export class MSGraph {
    public static async Get(graphClient: GraphHttpClient, url: string) {
        let values: any[] = [];
        while (true) {
            let response: GraphHttpClientResponse = await graphClient.get(url, GraphHttpClient.configurations.v1);
            // Check that the request was successful
            if (response.ok) {
                let result = await response.json();
                let nextLink = result["@odata.nextLink"];
                // Check if result is single entity or an array of results
                if (result.value && result.value.length > 0) {
                    values.push.apply(values, result.value);
                }
                result.value = values;
                if (nextLink) {
                    url = result["@odata.nextLink"].replace("https://graph.microsoft.com/", "");
                } else {
                    return result;
                }
            }
            else {
                // Reject with the error message
                throw new Error(response.statusText);
            }
        }
    }
}

You can use the code as follows where I have extended the properties to include the web part context object in order to pass in graphHttpClient object.

import { MSGraph } from './MSGraph';

...

try {
  let groups = await MSGraph.Get(this.props.context.graphHttpClient, "v1.0/groups?$top=5");
} catch (error) {
  console.error(error);
}

Monday, September 11, 2017

An approach to working with Schema Extensions in the Microsoft Graph

I wrote a post last week about my issues with custom metadata and the Microsoft Graph. The week ended leaving me on the fence on which way to go. However, a couple of days off has sorted my brain a little bit, and I’ve had dialogs with the Graph team on these three Stack Overflow questions:

I think I’ve finally landed on using Schema Extensions and the approach below seems like something I can work with.

Friday, September 8, 2017

Making ESPC as easy as 1.2.3 – get 10% discount on checkout

Europe’s Largest SharePoint, Office 365 & Azure Conference is approaching fast. I’ll be presenting How to Take Control of Your Office 365 Groups – Using Technology to Solve Business Processes, a developer centric session on how you can control Groups creation and configuration using PowerApps, Flow, Azure Functions and PnP.

Here’s a few handy tips on how to make the most of your time at the conference.

Find out who’s going

Check out Twitter #ESPC17 to find out who’s going or visit the ESPC17 delegates page. If you would like to be added to this page, email your image and details to sarah@sharepointeurope.com There’s no better time to network with your peers, connect with new prospects, or touch base with customers than ESPC17. Don’t bank on running into them at the conference, reach out to them before and arrange a meeting.

Plan Ahead

Take a look at the conference schedule and decide the sessions and tutorials you would like to attend. Take note of their time so you can plan your meetings accordingly. If you are travelling with co-workers, split up and attend different sessions. You can swap notes after, allowing your company to get the most out of the conference.

Learn

Before the session, think of some questions you would like the answers to. Don’t be afraid to ask them during the Q&A, or alternatively go up and have a chat with the speaker afterwards. It is also important to take notes. A good practice is to write down the 3 most important takeaways from each session.

Socialize

With 2,000 people from the SharePoint, Office 365 & Azure community estimated to attend ESPC17, it is worth going to the after parties (The Black & White Party at the Guinness Storehouse on Wednesday 15th) and the many other great side-line and networking sessions, you never know who you’ll meet. Swap ideas, get advice and make those all-important contacts. Don’t be afraid to go up to a speaker or blogger and introduce yourself – they expect this at a conference.

Share what you learn

To capture maximum value for you and your company, schedule time to share what you’ve learned and even better, to go ahead and implement, as soon as you get back to the office. Organise an informal meeting with your colleagues and managers and share important takeaways from the conference.

image

Still haven’t made up your mind? Then visit 10 reasons to attend ESPC17 to see why you should be there. Then book your ticket today. Use coupon code ESPC17Speak on checkout to avail of a further 10% discount.

Thursday, September 7, 2017

Which schema to use in the Microsoft Graph for custom metadata: Schema Extensions or Open Extensions? Or maybe neither? I need help!

[Read my follow-up post at: http://www.techmikael.com/2017/09/an-approach-to-working-with-schema.html]

I’m trying to build out a solution for custom metadata for Office 365 Groups and it’s not that easy to do the right thing. My scenario is some worker process setting data, so I’m running in an app only context, not user delegated.

SNAGHTMLb983c97
If you don’t know what I’m talking about, Schema Extensions and Open Extensions are ways to store additional metadata to objects in the Microsoft Graph. Objects can for example be a user account, an e-mail message in Exchange or an Office 365 Group object. You persist the metadata with the object, not in some outside store.

Using Open Extensions is by far the easiest, as you can define your extension and the key/value pairs of data arbitrary, using the permissions of the object you are extending. The caveat however is that you cannot filter on these metadata. Say you added metadata to e-mail messages saying it’s customer related. Using Open Extension you cannot query to retrieve only those messages tagged. For me, not being able to filter is a blocker.

This is where Schema Extensions come in. Schema Extensions are typed schemas and you can indeed filter on them. But here are the issues I have experienced so far – I might be wrong about some of these, and base them off my testing. There is a whole lot more information about schema extensions on the old Azure AD Graph docs – which may or may not have solutions to my issues.
  • Schema Extensions cannot be created using app only permissions, so you cannot really automate it, and you might need a separate ADAL app to create it from the one you use for other automation.
  • A schema can not be used before it transitions from InDevelopment to Available.
  • You cannot delete a schema which has transitioned into being Available.
  • You cannot delete properties, only add new ones once a schema is made Available.
  • You cannot add a schema which is InDevelopment to for example an Office 365 Group object, making development harder.
  • Unless you have a verified domain of com, edu, net, org, you will end up with a random prefix for your schema extension. Which means you cannot easily filter to find the schema you created, but need to loop over all to find the one ending in your name. Unless you choose to record the name, but that won’t work for a generic solution across tenants.
In order to get the most flexible solution you might want to put metadata you need to filter on in a schema extension, and all others as open extension data. But this increases complexity as data are stored in two ways. Not the most ideal way to go, but a possibility.

Summary

While storing metadata in the Microsoft Graph seems like a good idea, as it reduces the number of moving parts, it might not be the best solution for every scenario. The ALM scenario for me right now is a bit in the wind, and perhaps storing the data outside the Microsoft Graph is a better and more flexible approach – even though I have to add more moving parts to the solution.

What’s your take?

Wednesday, August 30, 2017

How to update a SharePoint Framework project to Drop 1.2 (Extensions RC0)

[Update September 2nd, 2017]

I’ve updated the steps to remove the node_modules forlder, as that seems to fix some of the build libraries.

--

With the release of SharePoint Framework Extensions RC, SPFx has moved to Drop 1.2. If you want to update an existing project these are the steps you need to take.

Update the generator to Drop 1.2

npm install -g @microsoft/generator-sharepoint 

Enter your SPFx project folder and open package.json, where you need to update the version numbers for the following packages:

dependencies

"@microsoft/sp-core-library": "~1.2.0",
"@microsoft/sp-webpart-base": "~1.2.0",
"@types/react": "15.0.38",


devDependencies

"@microsoft/sp-build-web": "~1.2.0",
"@microsoft/sp-module-interfaces": "~1.2.0",
"@microsoft/sp-webpart-workbench": "~1.2.0"

If you have other @microsoft packages, set them to 1.2 as well, or if it’s @microsoft/sp-client-base, you can remove it.

Next up, remove your node_modules folder with your favorite command before running:

npm install
gulp --upgrade

And you should be all set!

NOTE! You may run into other breaking API changes you have to deal with.

NOTE 2! If you are using Office UI Fabric with react, Drop 1.2 takes a dependency on v4.32.0, and not on v2.34.2. If you have an explicit link to v2.x, then you have to upgrade in order for the build to work. See this issue for information about Office UI Fabric React which I raised with Drop 1.2 https://github.com/SharePoint/sp-dev-docs/issues/827

Sunday, August 27, 2017

Maybe the most useful Azure function ever! – Introducing a proper Swagger definition generator

This post relates to Azure functions written in C#, hosted as a Function App – and maybe the title is a tiny bit clickbaity ;)

I’m in a project where writing small Azure functions to accomplish pieces of functionality is a very good fit, and the tasks will be connected in a workflow – Microsoft Flow or Logic Apps. With the latest update for Visual Studio 2017, creating an Azure Function project and publish the function to Azure is super easy.

The tricky part comes when you want to consume those functions somewhere else using a Swagger definition file to describe your API.

The people over at the Azure team has been kind enough to add functionality to automatically generate a Swagger definition. The problem is that the output of this preview functionality is, to put it in nice terms, the equivalent to a table of contents, where the book was left out.

image

There are some blog posts out there on how you can write that book to get a working definition, but manual work when you have already defined the functions pretty well in code is not my cup of tea.

Digression

If an Azure function project had been a WebAPI or similar, you could have installed Swashbuckle, and you would have gotten a nice Swagger definition just like that. The fact that Azure functions are compiled to a class DLL, and that Swashbuckle does not work against a DLL in any easy fashion, I saw two options. A shadow API with Swashbuckle, or roll my own.

I started out with the first option where I created a shadow WebAPI project, copying all my Azure Function signatures, and then manually copying out parts to generate a proper Swagger definition. But I quickly discovered this was still too much manual work for my taste.

Option two it is – generate the mofo myself!

That left me with option two, write my own Swagger generator. The Swagger spec itself is not too complicated, and as Azure uses v2, that’s what I set to use as well. I’m no stranger to reflection on .Net DLL’s, having worked with SharePoint for many years, as well as writing other types of generators, so that’s what I did.

I created an Azure function in my project which at run time reflects on the current assembly, finds all methods marked to be Azure functions, then inspect their ins and outs, and construct a full fledged Swagger definition.

It’s not complicated, but a bit tedious to support all the scenarios I wanted to support. I wanted to support input via the path, as query parameters and JSON objects in the body – which is the most useful one in my opinion. It took me one working day, 7.5h to have this up and ready to go, with ~400 lines of code. Imagine if the Azure team could have spend the same? But then again, I wouldn’t have this blog post :)

Thursday, August 24, 2017

SharePoint Query Tool moves to the PnP Tools repository and v2.7 released

As Codeplex is shutting down the repository for the SharePoint Query Tool has been moved over to the PnP Tools repo. I’m very happy about this move, and if you want to make changes or additions, feel free to make a PR.

Get v2.7 (link on the page)

The code was actually moved over in late May, but today I give you v2.7. There is one major new function, and one function removal.

Firstly, GQL support has been removed as Microsoft is removing support of this preview API.

Secondly I’ve had some requests about the login for SharePoint Online being tricky with cookies and multiple tenants, and lately those with single sign-on on their machines were force logged into their SSO domain.

In the connection dialog you can now choose SharePoint Online Management, which piggy backs on the way the SharePoint Online Management Shell authenticated.

This should give you an easy way to switch between tenants and accounts by re-clicking the sign-in button.

image

Monday, August 21, 2017

A workaround to support switching logins between tenants in Windows apps or PowerShell – Piggyback the Microsoft SharePoint Online Management Shell ADAL application

image

If you write Windows Applications or PowerShell scripts to connect to multiple tenants you have probably experienced the “That didn’t work” dialog where the web based login tries to log you in with some other tenant credential as cookies are persisted from an Internet Explorer or Edge session. This was a typical problem for the SharePoint Query Tool, and I managed to add some funky code to ensure cookies were ignored when the login window popped up.

However, recently I’ve had a couple of issues where people have domain/aad joined pc’s with single sign-on to Office 365. With this in place cookies doesn’t matter. The login dialog will always try to log you in with your current user when using IE/Edge, which happens to be the browser control available when programming in Windows. Hence, using the query tool was hard to use in these scenarios.

At the moment the Query Tool uses web browser login and a FedAuth cookie for authorization. The obvious workaround is to use an ADAL app and a Bearer token instead. This is quite easy to implement, but having people register a custom ADAL app per tenant to support this login flow seemed too cumbersome. And for those who know me, I always try to find an easy way out, and a clever work around came to mind.

Saturday, August 12, 2017

Dealing with package errors when building SharePoint Framework solutions

I’ve been working on a web part for a customer lately and was going to bundle it up and install it at the customers tenant. When I ran gulp --ship to prepare the bundle I got the following error:

[14:47:51] Error - [webpack] 'dist':
cylinder-configurator.bundle.js from UglifyJs
Unexpected token: name (finish) [cylinder-configurator.bundle.js:5976,6]

Something broke when the build process was running uglify-js. I opened by bundle in the temp\deploy folder and looked at line 5676. This was a reference to MathLab which was included my the npm package pica which I’m using in this project.

I figured as much as to solve my issue I needed to break pica out of the main build process, but how?

image

Again, my good friend Waldek pointed me in the right direction and he has actually written about this before, and the needed documentation can be found in the official SPFx documentation Add an external library to your SharePoint client-side web part.

Friday, August 11, 2017

Enable external sharing on Communication sites

By default when you create a Communication site, external sharing has been disabled. As Communication sites are not listed in the site collection admin UI you need to turn to code or PowerShell to fix this.

I’m using PnP PowerShell where you enable external sharing using the following code:

# Connect to admin site
Connect-PnPOnline https://contoso-admin.sharepoint.com

# Enable external sharing. Possible values are:
# ExistingExternalUserSharingOnly
# ExternalUserAndGuestSharing
# ExternalUserSharingOnly
# Disabled

Set-PnPTenantSite -Url https://contoso.sharepoint.com/teams/Communication201 -Sharing ExternalUserSharingOnly

Similar code for SPO commandlets would be, where we also allow anonymous sharing of documents:
# Connect to admin site
Connect-SPOService -Url https://contoso-admin.sharepoint.com

Set-SPOSite -Identity https://contoso.sharepoint.com/teams/Communication201 -SharingCapability ExternalUserAndGuestSharing

I chose to use ExternalUserSharingOnly as you cannot share the full site anonymously, only documents, and sharing documents anonymously from a Communication site might not be the most obvious scenario.

Note: You have to ensure that the sharing settings on your tenant allows the sharing capability you are setting for the site.

The next step is to invite your external users. If you try to share using the Share site button in the UI, this won’t allow adding an external user (per writing this post).

image
The Share button is disabled for external users.
image

Instead navigate to Site permissions and hit the advanced permissions settings link. Or tack /_layouts/15/user.aspx at the end of the URL of your site. This is the old SharePoint permission page where you can hit the Grant Permissions button, and fill in your external user. Be sure to check off the email invitation and pick which access level the user should have. I’m adding my external user as a visitor in this example.

image

When clicking the site link in the invitation e-mail my external user is now added to the site and can browse around. Notice that as an external user you get blue/black suite bar, as the tenant theme won’t show for external users.

image

Happy sharing!

How to list all Communication sites in your tenant

The unique identifier for a Communication site is the web template used, and the name of the template for Communication sites is SITEPAGEPUBLISHING.

With this piece of information there are a couple of options you can use to find all the sites.

The first is to iterate over all site collections, and filter on the web template property, the other is to use search.

imageTo list all sites you can use the SPO Management Shell, CSOM tenant API, or hope my PR at PnP PowerShell get’s accepted (https://github.com/SharePoint/PnP-PowerShell/pull/998)

Basically the CSOM code looks something like this:

SPOSitePropertiesEnumerableFilter filter = new SPOSitePropertiesEnumerableFilter()
{
    IncludePersonalSite = PersonalSiteFilter.UseServerDefault,
    StartIndex = null,
    IncludeDetail = true,
    Template = "SITEPAGEPUBLISHING#0",
    Filter = null
};

var list = Tenant.GetSitePropertiesFromSharePointByFilters(filter);

Using the SharePoint Online Management Shell do this:

Connect-SPOService https://contoso-admin.sharepoint.com
Get-SPOSite -Template SITEPAGEPUBLISHING#0 -Limit ALL

With the PR accepted you would be able to use PnP Posh with the following command to list all Communication sites much like the management shell:

Connect-PnPOnline https://contoso-admin.sharepoint.com
Get-PnPTenantSite -WebTemplate SITEPAGEPUBLISHING#0

If you want to use search, you can use the Submit-PnPSearchQuery commandlet.

Submit-PnPSearchQuery -Query "webtemplate=SITEPAGEPUBLISHING" -All -RelevantResults
This approach is of course available for other templates as well. Happy iteration!

Thursday, August 10, 2017

What is Microsoft 365?

image

I’ve read numerous posts after the release of Microsoft 365 at Inspire stating that Office 365 now has a new name. This is not the case.

Microsoft 365 is a combination of:

aka, a bundled offering, and is primarily target for businesses up to 300 users. The idea is that you per user per month pay for subscribing to Windows 10 as an operating system, Office 365 products and device and security management.

If you scroll far enough down on https://www.microsoft.com/en-us/microsoft-365/business you see a comparison of Office 365 Business premium vs. Microsoft 365 Business.

image

And if you scroll down on https://www.microsoft.com/en-us/microsoft-365/enterprise you see the Enterprise offerings in E3 and E5 SKU’s (but with no pricing).

image

Global deployment of SharePoint Framework Parts and Extensions is now live!

With version 1.1.3 of the SharePoint Framework yeoman generator it is now possible to specify for a SPFx solution to be tenant wide available. This means that when you upload the solution to your tenant you have the ability to let the extension or web part be made available to all site collections in your tenant.

image

Using Windows, in a console with administrative privileges you can get the latest version by re-installing the generator with: npm i @microsoft/generator-sharepoint -g

For web parts this means that the web part is available for use on all pages in all sites. For extensions, you still need to perform the part of registration using CSOM or REST per site collection/site/list where the extension is to be registered, but you don’t have to first add the extension to the site. For auto-provisioning scenarios this is huge!

Read the documentation over at https://dev.office.com/sharepoint/docs/spfx/tenant-scoped-deployment

YouTube video

Questions

Q: How can I make my existing SPFx solution be available tenant wide?

A: Update @microsoft/sp-build-core-tasks to v1.1.1.

> npm i @microsoft/sp-build-core-tasks

Open up config/package-solution.json and add "skipFeatureDeployment": true to the solution element.

{
  "solution": {
    "name": "tenant-deploy-client-side-solution",
    "id": "dd4feca4-6f7e-47f1-a0e2-97de8890e3fa",
    "version": "1.0.0.0",
    "skipFeatureDeployment": true
  },
  "paths": {
    "zippedPackage": "solution/tenant-deploy-true.sppkg"
  }
}

Q: Do I need this feature for web parts?

A: If you create web parts which should be globally available, this is the feature for you. If you create custom applications, then stick to the old way of adding it where needed.

Q: Do I need this feature for extensions?

A: Definately! This feature was made for SPFx extensions. You could for example create an extension with a company footer which you want available on all sites in your tenant. You would still need to figure out the best way to register the extension on the sites – but that is something we can deal with!

Wednesday, August 9, 2017

How to reduce the bundle size when using Office UI Fabric with React in SharePoint Framework

image

Yesterday I wrote about how the build process has changed for SharePoint framework web parts using the Office UI Fabric components and Fabric Core CSS. Previously you could take a dependency on whatever version was deployed in your tenant and your web parts were quite small in size as Office UI Fabric was pre-loaded from a Microsoft CDN. This has now changed, where the build process will bundle in the Office UI Fabric components to ensure your part works regardless of what Microsoft updates.

The caveat of this is that your web part grows in size (considerably), and if you have more than one web part on the page, all using Office UI Fabric components – well, load times do go up as code is loaded multiple times.

There’s a perfectly good reason for this change. Microsoft might decide to upgrade components they use, which could break your solutions. This means that you as a developer must control what version of Office UI Fabric you are using.

More: Read about it at https://dev.office.com/sharepoint/docs/spfx/web-parts/guidance/office-ui-fabric-integration

One solution to get the bundle size down again and at the same time have full control is to put Office UI Fabric components and CSS on a CDN, and having all parts re-use this. Fortunately I have done this for you :)

Tuesday, August 8, 2017

SPFx bundle size when using Office UI Fabric React components

I was making some updates to a SharePoint Framework web part which utilized the React Office UI Fabric components today. Previously when I built the web part, the bundle .js size was about 10kb. When I built it today the size was suddenly close to 600kb.

image

The reason turns out to be how the build process works in SPFx. It now bundles Office UI Fabric components instead of re-using whatever SPO has installed. It actually makes sense to de-couple the dependency and ensuring your web parts won’t break if Microsoft decides to upgrade Office UI Fabric on their side.

Ideally I would like to externalize Office UI Fabric to a version which I control. Reading up on How to use the Office UI Fabric React in a Safe Way in Your Solution we’re told to put an explicit dependency in your project to Office UI Fabric, so you have control over the version. The biggest issue right now might be that can you really expect an average developer to hit that doc page? Hopefully the version will be added in packages.json in a future version of the SPFx generator. (See the end of this post for how to add the reference.)

Friday, July 14, 2017

Adding custom managed properties to the default display templates – the quick and dirty way for non-developers with little time on their hands

Yesterday I was helping another consultant with some search rank issues. We had an hour scheduled and managed to resolve the ranking well enough. But then I got the question, do we have time to add some custom data to the results? Ouch! Something which sounds easy, and is not done in 5 minutes. Turns out after I gave it some thought on an upset stomach due to a bad kebab yesterday, it is actually a 5 min task when you know how.

Read on!

The search page was a typical custom search page created in a team site as a web part page, with three search web parts and a scoped query. Very easy to set up in a few minutes. The results were Office documents and PDF’s scoped to a few specific locations.

image

There are numerous posts out there on how to add custom managed properties, but that involves duplicating all the default display templates, or creating a mother of all template which renders all file types the way you want.  If you try to modify one of the default display templates to add your custom managed property, it just won’t load.

So why this post? Well, I’ll let you know how you indeed can modify the default templates, and still get your custom managed property to render. It might not be the proper or recommended way, but it works and will save non-devs tons of time making minor changes to the search results.

A typical result looks like the image below, and we want to add some piece of metadata at the red arrows.

imageShow me how already, will you!

Start off with downloading a copy of Item_CommonItem_Body.js from /_catalogs/masterpage/display%20templates/search/item_commonitem_body.js in your site collection. This is a shared template used by all the other ones.

Around line 120 (at least on my recent download), add the code to show after the path in green. As my example I’m adding values from a managed property named encoding, which is not one of the default ones available in the results. I do this my adding the highlighted line below:

ms_outHtml.push('       '
,'            <div id="', $htmlEncode(id + Srch.U.Ids.preview) ,'" class="ms-srch-item-previewContainer"> '
,'                ', previewHtml ,''
,'            </div>'
);
        }
        
        ms_outHtml.push('Encoding:' + $getItemValue(ctx, "encoding"));
        
ms_outHtml.push(''
,'    '
);

Upload the updated Item_CommonItem_Body.js, overwriting your existing one. Make sure it’s published.

Your results will now show the label, but no value for encoding.

image

So how do you ensure loading of your custom properties? Basically you modify a web part property called SelectedPropertiesJson on the search result web part. You can download the .webpart file and modify it manually, or you can for example use PnP PowerShell.

image

The below code ensures loading of two managed properties, encoding and refinablestring100.

Connect-PnPOnline -Url https://tenant.sharepoint.com/sites/searchsite

$webPartName = "Search Results"
$pageUrl = "/sites/test2/SitePages/My Search Page.aspx"

$wpId = (Get-PnPWebPart -ServerRelativePageUrl $pageUrl |? {$_.WebPart.Title -eq $webPartName}).Id
$myProps = @("encoding","refinablestring100")
$json = ConvertTo-Json $myProps -Compress
Set-PnPWebPartProperty -ServerRelativePageUrl $pageUrl -Identity wpId -Key "SelectedPropertiesJson" -Value $json

If you refresh the search result page, it’s now also displaying your custom properties!

image

Wednesday, July 12, 2017

Every (blank) pixel counts! - Minor quirk on custom ribbon action icons in modern lists

Typically the icon for a custom ribbon action in classic pages is 32x32 pixels. On modern lists the icon however is 20x20 pixels. If you use a 32x32 pixel image this is what it will looks like in classic and modern view.

image

Notice in modern view how the icon goes below the base line of the default ribbon icons, and that the icon is a little fuzzy. This is due to scaling 32x32 down to 20x20, which is not a direct pixel multiple. The solution is to pad the 32x32 image to 40x40. Keep the icon the same size, but increase the white space.

Original 32x32 icon

image

Padded 40x40 icon

image

In classic mode, the icon will look the same due to the use of CSS to position the image, where any overflow is hidden. In modern view however we now get a crisp image aligned with the existing icons. The crispness is due to the image being 40x40 pixels, and divided exactly in half – which avoids pixel smoothing on resize.

image

This might not be a big deal, but every pixel counts right? Winking smileGoing forward for modern pages you would use SharePoint Framework extensions for ribbon actions instead which allows the use of Office UI Fabric font icons instead of image files.

Monday, July 10, 2017

Querying managed metadata using REST (unsupported)

I’m working with a set of SharePoint framework web parts and finally got fed up of using JSOM to work with managed metadata. Referencing JSOM feels wrong for so many reasons in this modern day and age, so I’d rather not do it.

I mean, when the out-of-the-box modern controls in SharePoint can work with terms using REST, why shouldn’t I? I like to believe I’m pretty modern.

image

After Fiddling the modern UI I found out that all taxonomy requests are working against a service at /_vti_bin/TaxonomyInternalService.json. This is a good old service, also listed on MSDN at https://msdn.microsoft.com/EN-US/library/microsoft.sharepoint.taxonomy.webservices.taxonomyinternalservice_methods.aspx. It is indeed marked for internal use, and until we get a new public REST point, everything points to that it should indeed work. If not, why would Microsoft use it themselves?

I have put together a sample SPFx web part based on the MSDN documentation, trial and error, showcasing different methods I found useful when retrieving terms. It’s by no means complete, but should help you get started.

You can find the sample at https://github.com/wobba/spfx4fun/tree/master/react-taxonomy-rest (link to all methods)

Samples include:

  • List term stores
  • List term groups in a term store
  • List term sets in a term group
  • Search for term by label
  • Search for term sets by name of term set or term in set
  • Get level one terms of a term set
  • Get child terms for a term

Using these calls you should have enough information to build a metadata driven megamenu, or build a taxonomy picker.

I have included interfaces for all the REST calls with some comments, but not for the returned results. You might have to examine the result JSON and pick out what you need.

And as a nugget I output the JSON with indentation, a tip I picked up from Sahil Malik :)

Sample interface

interface IGetSuggestionsRequest {
  start: string; // query
  lcid: number;
  sspList: string; // guid of term store
  termSetList: string; // guid of term set
  anchorId: string;
  isSpanTermStores: boolean; // search in all termstores
  isSpanTermSets: boolean;
  isIncludeUnavailable: boolean;
  isIncludeDeprecated: boolean;
  isAddTerms: boolean;
  isIncludePathData: boolean;
  excludeKeyword: boolean;
  excludedTermset: string;
}