The blog of Wictor Wilén

  • How to generate SharePoint Framework bundles for multiple tenants

    Tags: SharePoint Framework, npm, SharePoint

    If you are an ISV or SI with multiple clients and are interested in building SharePoint Framework (SPFx) solutions that you would like to re-use you will face a huge issue when it comes to reference SharePoint JavaScript files and reference your SharePoint Framework bundles. All these URL's are hardcoded into your solution configuration files and requires you to update these files and rebuild for each and every client environment. And not only that even in your own development team this will cause issues if you don't have a shared development environment.

    This causes a lot of issues and headaches. Each and every developer needs to update the configuration files in the SharePoint Framework - meaning that they will check-out the files and then eventually check them back in with their specific tenant information, which will break the solution for another developer. Same goes if you want to deploy a solution to another client; you check the files out update with the new client information and the more clients you have the worse it gets.

    The SharePoint Framework is essentially built so that you should NOT reference any SharePoint JavaScript files (think CSOM/JSOM) and always host your bundled SPFx files in a public CDN. In practice this doesn't work. There are tons of features in JSOM that you would like to use, such as managed metadata. Also very few clients really want their JavaScripts to be hosted in a location they don't own or have control of.

    So, SharePoint Framework as of now is very limited and it is a mess for you as a developer, SI or ISV. I know, that's exactly where I've been, until now!

    Introducing the spfx-build-url-rewrite node package

    To sort this issue out I've built a node.js package called spfx-build-url-rewrite that helps you re-write those URLs at build time. All it requires is that you in your config files use a specific URL that the package knows about (currently it's contoso.sharepoint.com - I know, I'll make it configurable/better later) and when building you specify the URL you want to replace it with, and voila - you can now automate builds for any number of clients/environments.

    How it works

    First of all you need to install the node module into your SPFx solution using npm:

    npm install spfx-build-url-rewrite --save

    Then you need to modify the gulpfile.js to use this module. Just before the initialize method you need to add two lines so it looks like this:

    const rewrite = require('spfx-build-url-rewrite');
    rewrite.config(build);
    
    build.initialize(gulp);

    Whenever you want to reference a script inside SharePoint, such as the JSOM files or you want the SPFx CDN to be in SharePoint you modify the config.json or write-manifest.json files to use https://contoso.sharepoint.com instead of your tenant URL.

    config.json

    externals in config.json

    write-manifest.json

    cdn base path in write-manifest.json

    Now when you build the solution you append the argument --target-cdn <url> to replace the URLs in your solution, as follows:

    gulp build --target-cdn https://fabrikam.sharepoint.com
    gulp bundle --target-cdn https://fabrikam.sharepoint.com
    gulp package-solution

    If you don't want to specify this for each and every command you can create an empty file called .env and specify the substitution URL in it like this:

    TargetCdn=https://fabrikam.sharepoint.com

    Summary

    I hope this small node package makes your life easier, it sure makes mine! If you have any feedback please use the Github repository.

    And as a final note, even though it is supported to extend the build pipeline of SPFx this is possibly in the grey zone - but it works…on my machine.

  • yo teams: a full Microsoft Teams extensibility Yeoman generator

    Tags: Microsoft Teams, Bot Framework, TypeScript, Yeoman, Office 365, Gulp, Git

    A couple of weeks back I published a Yeoman generator to build Tabs for Microsoft Teams. Since then I've continued to add stuff to it as the Teams team has continued to add features to their extensibility story. So, this generator is not only for creating Tabs, but now also for adding Bots and Custom Bots to Microsoft Teams. With that I decided to rename the generator to yo teams (generator name is generator-teams).

    I'm very thankful to the over 600 downloads within less of a month, and all the positive feedback, the issues and PR created. Keep it coming.

    What's new?

    The two big new features of the generator is the ability to add either a reference to an existing bot that you want to use (your own, or any bot in the Bot Framework directory) or to create a bot from scratch, using the Bot Framework. You can also add a custom bot, which is a Microsoft Teams specific webhook that acts like a bot, which can be used by specific Teams only and you don't have to add it to the Bot Framework - perfect for those internal smart bots you want to build.

    yo teams

    The source code has also gone through some heavy refactoring with sub-generators and all. There's more to come…

    Both the Bot and Custom bot uses the JavaScript/TypeScript implementations of the Bot Framework and you have some boilerplate code to get started, including readme files of the essentials.

    If you have any issues, or feedback, or problem or just feeling chatty, then use the Issues list on Github.

    What do I need to do?

    If you already used the old generator, uninstall it with npm uninstall generator-teams-tab --global and then install the new generator with npm install generator-teams --global. All your current solutions will work, but I recommend you to, if you feel like it, to "move" all your code over to a newly created project.

    The old npm package is deprecated and you will get a warning if you try to install it.

    The Github repository has been renamed, but the old one will still redirect you to the correct location. You can find it here: https://github.com/wictorwilen/generator-teams

  • Congratulations to the Microsoft Teams team on an excellent delivery

    Tags: Microsoft Teams, Office 365

    A big round of applause for Microsoft and the team behind Microsoft Teams for now being general available (GA) worldwide. Today, they lit up the Teams icon in the Office 365 waffle for all tenants (unless your admins are being boring and has turned it off).

    image

    It's been awesome to be a part of this preview journey, which started last summer. Avanade was selected as one of the TAP members, in a preview program shrouded in a secrecy I've not seen at Microsoft before. Our IT department slowly trickled it out, so that we had a chance of learning how Microsoft Teams could fit into our organization and our way of working. A big thanks to David who have mastered the preview program internally.

    When Microsoft Teams was unveiled to the public, back in November, we did our first point-of-view based on our experience so far. Since then I would say that the way we work has changed dramatically. Many of our teams and interest groups has quickly adopted this new chat-based workspace - not just for chatting but as the preferred channel for communication and collaboration. It fits our style of work perfectly, given how spread out our teams are and the different time zones we're working in.

    Personally I've been way more effective in my work since we started to adopt Microsoft Teams. The number of unnecessary e-mails has gone done dramatically, my inbox is not flooded with simple questions, or links, or things that can more easily be expressed through a chat. One thing that has surprised me is how much more we use the ad-hoc chats compared to what I've expected, we don't all have to be online at the same time - you can easily go back and see what's been discussed while you were away or in a meeting. Sharing of files and notes is so much easier now and it allows us to have an ongoing discussion about them.

    It's been a blast discussing Microsoft Teams with my clients. And I'm thrilled that some of them now are leading with "Teams first" - that is you create a Team, not a SharePoint team site, not an Office 365 Group. You get them for free with Teams anyways. This will change the way collaboration is done for enterprises going forward, without doing trade offs for compliance, governance and security.

    This is the first release of many to come. And they number of features that has popped up over the last few months are incredible. And I'm sure we will see some more productivity enhancers going forward.

    Once again, thank you to the team behind Microsoft Teams.

  • yo teams-tab: A Microsoft Teams Tabs Yeoman generator

    Tags: Microsoft Teams, Office 365, Yeoman, Git, Gulp

    I'm happy to announce that today at SharePoint Saturday Munich I presented a new Yeoman generator for building Microsoft Teams Tabs projects. Tabs in Microsoft Teams is a great way to extend the user interface and to do integrations to other systems and provide visualizations. Tabs are based on a JavaScript framework, a set of web pages and a manifest describing the Tab. It requires a set of manual steps to both build out the pages, configuring CSS, hooking up the JavaScripts, deploying it all to a web site hosted in the cloud, writing the manifest, packaging the manifest into a zip file and more.

    With the Teams Tab generator you can in an easy manner scaffold out the project and get a build and deployment pipeline, and be up and running in a few minutes.

    yo teams-tab

    The project that will be created is a TypeScript based project with a set of Gulp tasks to build the project and package the manifest, and optionally a built-in Express server to host the web sites and configuration so that you can with a simple command deploy your project to an Azure Web App.

    How to get it

    The generator is published as an npm package and you use npm to install it. The following command will install it as a global package for you to scaffold your Teams tabs.

    npm install generator-teams-tab --global

    How to use it

    To create a new Teams Tab all you need to do is open up a command prompt and use Yeoman to create the project. The generator will ask you a set of questions and your project will configured based on those.

    yo teams-tab

    How to use the project

    The project contains all source code you need to build and deploy tabs. Use Visual Studio Code or whatever text editor you prefer. The source code is divided into two areas. The first one being the actual tabs (pages and scripts), located in ./src/app You will find one folder called web which contains the web pages required for a tab; such as the actual tab page, the configure page and remove page. In the scripts folder you will have the TypeScripts file in which you build the logic for your tabs. For instance the actual main tab page, tab.html, has a corresponding tab.ts TypeScript file. You'll get it…

    In the ./src/app folder there is also a TypeScript file called server.ts. Note, this file only exists if you answers yes to the question on using Express to host the Tab. This file is the server side node.js web server. If you need to modify the paths or want it to do fancier stuff than just client-side scripting this is where you start hacking.

    There's also a folder calle ./src/manifest which contains the Tab manifest. A json file you might want to configure. And that folder also contains the two images you need to have for a Tab.

    The tab project

    How to build it

    You build the tab by using a simple Gulp task that will transpile and bundle your TypeScript into JavaScript and sets up the web server. Just use the following command to build it

    gulp build

    Once you've built it you can follow the instructions in the README.md file to deploy it to an Azure Web App.

    The manifest for the tab is created by using another Gulp task:

    gulp manifest

    This task will create a zip file that you use to upload to your Teams team and it references the specified web site hosting the tabs. The file being created is located at ./package/tab.zip

    This is great, but I want to…

    I know, you want to have more stuff in the generator. It's all available on Github for you to grab and hopefully come back with suggestions. Go git it here: https://github.com/wictorwilen/generator-teams-tab

    I'm looking forward to feedback and I'll keep updating the generator in line with what the Teams team are doing with their JavaScript framework, which is currently at 0.4.

    glhf

  • SharePoint Framework has now reached General Availability - such a great journey

    Tags: SharePoint Framework, Office 365, SharePoint

    Let me start with congratulating the SharePoint Framework team on an amazing job and an amazing journey reaching this GA milestone.

    The SharePoint Framework plays a significant part of the SharePoint future, yes - this is only the first version with a lot of new features on the way, and it is a part of the new SharePoint wave. I've haven't seen this interest in SharePoint for many years and I'm glad I'm still in this business. Delivering top notch collaboration solutions for our clients at Avanade. The SharePoint Framework will make it easier for us to customize SharePoint and it will also bring a lot more value for our clients in the end allowing them to stay evergreen and not being tied into "workarounds" and pesky SharePoint Designer hacks or arbitrary JavaScript snippets.

    I'm extremely glad that I've been a part of this journey, seeing the team making the awesome stuff they've done. For me it started back in the fall of 2015 when we we're shown some very early ideas on where to go next and also some whiteboard sessions where we had an open and frank discussion about what the requirements were from the field. This openness is something that I think has made the difference this time and made the SharePoint Framework into what it is. All discussions was kept very secret and I'll tell you it was hard not to cry out to everyone how excited I was on the progress.

    Early 2016 I was part of the first DevKitchen, where we had the opportunity to use SharePoint Framework for the first time. The team had only in a couple of months created something that actually worked! It was very satisfying to build that first web part (I do think that I was the first outside of Microsoft that actually built a client-side Web Part!).  The framework had its quirks and issues back then, but they kept the speed up and delivered. A few more DevKitchens were hosted and finally in May they revealed the SharePoint Framework to the public.

    Just after the summer everyone could get their hands on the first public release of the SharePoint Framework and the SPFx Team opened the floodgates of feedback through their Github repository. It has been fantastic to see all the support, wishes, bugfixes, samples and documentation that the public (and specifically Waldek) has produced to support the SharePoint Framework. And how fast and agile the team has responded to all the requests and issues. This is how Microsoft should build more stuff!

    I've tried to do my best giving feedback as a consultant, developer and things my clients need. I'm particular proud of the enterprise guidance documentation that I've helped with. I absolutely love being part of this community.

    Now, we're here, within a few weeks all tenants in Office 365 should be able to use the SharePoint Framework to build great stuff and awesome client-side Web Parts. We're already in the midst of porting our solutions to take advantage of the SharePoint Framework and getting it in the hands of our clients.

    Thank you to the SharePoint Framework Team - I'm so looking forward to what's happening next. See you in a few weeks in Redmond ;-).

    Let's make SharePoint great again!

  • Configuring Office 365 Groups creation the right way

    Tags: Office 365, Office 365 Groups, Azure AD

    Over the last few days the issue on how to prevent users to create Office 365 Groups has popped up in all sorts of conversations. This blog post will show you how to do it in the correct way, and serve as a future reference. I'm not the only one who have blogged about this, it's in many places including official documentation. But in many places both scripts and some caveats are either wrong or outdated. One post covers this topic really well, and in a good and correct way and it's this post by John P. White - Disable Office 365 Groups, part 2. Read it! This post however will show you how to do it in a more direct way, using PowerShell.

    Background

    We used to prevent end-users from creating Office 365 Groups (from now on referred to as only Groups) using an OWA Mailbox policy. Even I have a blog post on that topic. But this way to do it is outdated. That mailbox policy only applies to Groups being created from OWA (Outlook Web Access, Outlook on the web…whatever) and Outlook. It did not prevent people from creating Groups using Microsoft Teams, Planner, StaffHub, PowerBI, Dynamics 365 and what not.

    How to do it properly

    Instead of continuing to building the settings on the Mailbox policy setting, this setting has now moved to Azure AD. You can even see it in the "new" Azure Portal, although it doesn't really reflect the real settings and not all settings.

    Azure AD Settings for Office 365 Groups

    The way to do it is to use PowerShell and essentially follow the official documentation. The problem with that article however is that it contains a few errors, is not updated, has some weird scripts and is just to darn long to read through. So, here's a my PowerShell for this. You can find the complete script in this Gist.

    Prerequisites

    To be able to run the PowerShell you need to install some stuff

    • The Microsoft Online Services Sign-in assistant
    • The Windows Azure Active Directory Module for PowerShell - and here's a big thing. You MUST (at the time of writing) only use the preview version, with version number 1.1.130.0-preview found here. Do not try to download the higher version with version number 1.1.166.0 - it will not work.

    Now, we got that out of the way, let's get to the fun stuff.

    Scripting FTW

    First we need to log in to our tenant using an admin account. I prefer to use a the Get-Credential method over the dialog option, makes everything more smoother.

    # Store the credentials in a variable
    $creds = Get-Credential
    
    # Connect to the Microsoft Online services
    Connect-MsolService -Credential $creds 
    
    

    The next thing is to make sure that users are allowed to create Groups, we'll limit it later. Make sure you use the script below and not the one in the official article as they have spelling errors on the variable.

    # Get tenant setting (misspelled in official docs)
    Get-MsolCompanyInformation | Format-List UsersPermissionToCreateGroupsEnabled
    
    # If false, then use the following
    Set-MsolCompanySettings -UsersPermissionToCreateGroupsEnabled $true
    
    

    To limit the users allowed to create Groups we need to have a security group with members in Azure AD. And we need the Id of that group, so we'll grab it with some PowerShell:

    # Retrieve ID of Group that should have the option to create groups
    $group = Get-MsolGroup -SearchString "Group creators" 
    
    

    The settings we need to set are contained in an Azure AD object, created from a template. We retrieve that template using the following command and create our settings object like this:

    # Retrieve the Group.Unified settings template (assuming you have not done this before)
    $template = Get-MsolAllSettingTemplate | Where-Object {$_.DisplayName -eq "Group.Unified"}
    
    # Create the settings object from the template
    $settings = $template.CreateSettingsObject()
    
    

    Once we have the settings object, we can start setting properties.

    • EnableGroupCreation - should be set to false. We negate the tenant setting here, and we'll override it soon again for the specific security group
    • GroupCreationAllowedGroupId - this is the Id of the security group that are allowed to create Groups
    • UsageGuidelinesUrl - a URL pointing to your usage guidelines. Optional, but recommended
    • GuestUsageGuidelinesUrl - a URL pointing to usage guidelines for external users. This link will be shown in the external sharing e-mails and should of course be on a public available location. Optional, but recommended
    • ClassificationList - a comma separated list with your classification labels. Optional. Currently the first one in the list will be the default one. (does not work in all tenants at the time of writing)

    There's some more properties that you can take a look at, and over the last few weeks even some more popped up (without any documentation).

    # Use this settings object to prevent others than specified group to create Groups
    $settings["EnableGroupCreation"] = $false
    $settings["GroupCreationAllowedGroupId"] = $group.ObjectId
    
    # (optional) Add a link to the Group usage guidelines
    $settings["UsageGuidelinesUrl"] = 
      "https://contoso.sharepoint.com/Pages/GroupUsageGuidelines.aspx"
    
    # (optional) Add a link to Guest usage guidelines
    $settings["GuestUsageGuidelinesUrl"] = 
      "http://contoso.com/usageguidelines"
    
    # (optional) Add classifications to be used for Groups
    $settings["ClassificationList"] = "Public,Internal,Top Secret"
    
    # Verify
    $settings.Values
    
    

    Now we have the settings and all we need to do is to add them to Azure AD:

    # Add the settings to Azure AD
    New-MsolSettings -SettingsObject $settings
    
    

    And from now on, only members of the security group can create Office 365 Groups using all endpoints such as Planner, Teams, PowerBI, Microsoft Graph REST etc. BUT StaffHub still ignores this setting!!!!! Aaargh!

    Need to update the settings?

    If you need to update the settings, or there are new properties that you want to configure, then use the PowerShell below. The one(s) in the official documentation is really weird written…

    # Retrieve settings
    $settings = Get-MsolAllSettings | Where-Object {$_.DisplayName -eq "Group.Unified"}
    
    # Check the values
    $settings.Values
    
    # Update a property
    $settings["GuestUsageGuidelinesUrl"] = "http://www.wictorwilen.se"
    
    # Save the updates
    Set-MsolSettings -SettingId $settings.ObjectId -SettingsValue $settings.GetSettingsValue()
    
    

    Summary

    That's it. It's not rocket science. Looking forward to further settings and also a proper UI in the Azure portal for the lazy people.

    The PowerShell is a bit weird though, should have had a review by the PowerShell team before going into the production in my opinion.

  • SharePoint Framework: how to properly dynamically populate dropdown property pane fields

    Tags: SharePoint Framework

    One of the key parts of SharePoint Web Parts is the ability to have them configurable using the Web Part properties. This story is still true with client-side Web Parts in the new SharePoint Framework. In this post I will show you one of the more common scenarios; how to populate drop downs (and other fields) in the property pane dynamically. But also show you how what's wrong with the current implementation.

    Client-side Web Part Property Pane basics

    The Property Pane in client-side Web Parts are defined by the propertyPaneSettings method. It's a method that returns an IPropertyPaneSettings object representing the pages, groups and fields. This method is invoked whenever you click to edit the Web Part. The method is "synchronous" meaning that you should return a static set of pages, groups and fields - there's no option to wait for it to load (SPFx issue #127).

    Defining the Dropdown

    For a Dropdown we use the PropertyPaneDropdown field and specify for instance it like this, in the propertyPaneSettings method.

    PropertyPaneDropdown('listName', {
      label: strings.DescriptionFieldLabel,
      options: this._options
    })

    In this case the options property references a local variable defined as below:

    private _options: IPropertyPaneDropdownOption[];

    Loading the data…

    But when do we load this data into the local variable. We do have a couple of options to do this, but there really is only one viable option at the moment and that is to use the onInit<T>() method. This method always runs when the Web Part is loaded or is added to a page, and it only runs once and it is the only option we have if we want to load something using a promise and make sure to have it loaded before we have the chance of rendering the property pane. The onInit method returns a Promise and that allows us to actually block the loading of the Web Part until we have the data.

    To load data, mock or live, I've created a simple list data provider, that you can find in the following Gist. Pay attention to the fact that I have set a delay on the mock loading to 5 seconds, this is to simulate a slow request and show you how bad this current implementation actually is. https://gist.github.com/wictorwilen/0bf866ee4fda2f9611f43ee17b9127d5

    Then I implement the onInit method as follows:

    public onInit<T>(): Promise<T> {
      let dataService = (this.context.environment.type === EnvironmentType.Test || this.context.environment.type === EnvironmentType.Local) ?
        new MockListsService() :
        new ListsService(this.context);
    
      this._options = [];
    
      return new Promise<T>((resolve: (args: T) => void, reject: (error: Error) => void) => {
        dataService.getListNames().then(lists => lists.forEach(list => {
          this._options.push(<IPropertyPaneDropdownOption>{
            text: list,
            key: list
          });
          resolve(undefined);
        }))
      });

    First of all I create my data service object (mock or real one). Then I create a new Promise that uses my list service and once the list data is read it populates the local variable holding the data for our dropdown and finally I resolve the promise.

    There are some articles and Github repos that uses another approach, they just return a resolved promise and let the call to the external data service run in the background. If that background request takes to long time (the simulated 5 seconds for instance) or fails then the user might have time to open the property pane and see an empty dropdown.

    Now when the web part is added to a page or a page a loaded with that web part the onInit method will "block" the web part from rendering until it has the data for the property pane. Well, that's not good, but that's how it is at the moment. There is however a possibility that we might be able to use displayMode property of the web part and only do this when we are in edit mode. I have not been able to verify this as the workbench always are in edit mode and none of my tenants actually loads the client side web parts in the modern pages - I'll get back on this.

    Another annoying thing is that since this is blocking the rendering, not even showing a spinning wheel or something, the user experience is quite bad if you have long running calls since nothing is shown (SPFx issue #228).

    Summary

    I've now shown you how to pre-load data for your property pane fields, yes same approach works for other field types than dropdowns as well. But is this the proper way then? Nope, it isn't but I have great hopes that this will be fixed in a better manner (see linked issues). If not we still have the option of creating completely custom property pane fields that allows us to do basically whatever we want.

  • SharePoint Online CDN features announced in preview

    Tags: SharePoint Online

    Today, Mr Vesa, announced the availability of the (long awaited) CDN features for SharePoint Online. The SharePoint Online CDN features allows you to turn one or more libraries in your SharePoint tenant into a repository for assets that you want to store in a CDN for performance reasons and geo-distribution reasons.

    SPO Public CDN

    How to set things up

    I'm not going to rehash everything that is outlined in the announcement post, but rather highlight a few important things.

    To get started and turn one of your libraries into a CDN enabled library you need to install the latest SharePoint Online PowerShell cmdlets to get access to some new and updated PowerShell cmdlets. Once you have that all you need to do is first enable it on your tenant and then add libraries for cdnization:

    $creds = Get-Credential
    Connect-SPOService -Url https://contoso-admin.sharepoint.com -Credential $creds
    Set-SPOTenant -PublicCdnEnabled $true
    

    Once this is done then you choose what libraries to use as the source for your CDN. You can point directly to a library or a subfolder in a library.

    New-SPOPublicCdnOrigin -Url https://contoso.sharepoint.com/SiteAssets/
    

    When you do this you get a big and yellow warning stating that everything you add in this folder from now on will be pushed to the CDN, and that CDN is publicly available and not governed by all the nice policies we have in Office 365. So, don't put your annual report drafts in there!

    Note, it might take a few minutes until it is all ready for usage after adding or changing the origins. 15-20 minutes is not uncommon.

    You can always retrieve all your CDN origins by using the following command:

    Get-SPOPublicCdnOrigins
    

    This command will list the ID of each CDN origin as well as the origin. The ID is very important, because you need that when you construct the URL to the CDN. It's probably one or more Guids in that ID…

    How to access the files in the CDN

    To get access to the files in the CDN you need to construct a path using the ID for the CDN origin like this:

    https://publiccdn.sharepointonline.com/tenant.sharepoint.com/ID
    

    For instance

    https://publiccdn.sharepointonline.com/contoso.sharepoint.com/1544004027c884490c55638fcb53de1f5d4897c5ea7513c1c0849dd7d8f4314cbda99429
    

    Yes, it's a bit long but that's how it is.

    If you now try to access an image or script in this CDN by just typing the URL in the browser, you will get an error stating "Invalid referer". This is by design and you need to add a HTTP Header to see the image/script, the Referer header with the value of the URL of your tenant (see announcement blog). In cases where you embed the image in a page or load the script from a page, this is done automatically for you by the browser. An even easier way is to just create a page and insert a Picture from Address, and paste your CDN Url.

    Caveats

    There's a couple of caveats to the CDN as of now. One thing I had hoped was that you could use image renditions in combination with this, which would have been AWESOME. Nope, you can't. Vesa, can you add that to the todo list?

    [Update 2016-09-29] The image renditions are partly supported, you can now use width, height and cropMode (fit for instance) as query string parameters for the image. But not RenditionId - once that can be done this feature is rock solid!

    [Update 2016-09-29] CORS support has been enabled! Thanks you to the product group for fast and quick turnaround for these kind of features!

    Another quite important thing is that the CDN isn't CORS enabled. They do a check for the referer, see above, so that the request are coming from the correct tenant. But if you try to use jQuery or some other mechanism to load scripts or assets from the CDN location you get

    No 'Access-Control-Allow-Origin' header is present on the 
    requested resource. Origin 'https://tenant.sharepoint.com' is
    therefore not allowed access.
    The response had HTTP status code 406.

    I wish they added the possibility to allow CORS requests from the tenant, just as they only allow request refering from the tenant.

    Summary

    I think this feature is awesome and I have already upgraded a few tenants and customers to use this new feature for some embedded resources. Oh, did I say this is free of charge and a part of your Office 365 subscription. Free beer is good beer, despite in preview!

  • SharePoint Framework Nuggets: working with GUIDs

    Tags: SharePoint Framework

    SharePoint developers - we do like GUIDs, don't we. We all read RFC4122 both once and twice. And now with SharePoint Framework and the goal to embrace all them Macintosh and open source people - they gotta have their fair share of GUIDs.

    And to aid with that the SharePoint Framework got some really nice GUID features, although a bit unpolished as you might notice - but this is all preview bits at the time of writing.

    SharePoint Framework Nuggets - GUIDs

    The Guid class

    First of all we must have the option to create GUIDs. There might be a situation that you need to store something really, really unique - to help with the we do have the Guid class in the @microsoft/client-sp-base module. This class both allows you to create new Guids, parse Guids and validate Guids.

    This is how you import the Guid class:

    import { Guid } from '@microsoft/sp-client-base';

    Then to create a Guid, you use the code below. The newGuid method can take an optional parameter if you need to create a custom random generator. By default it generates a version 4 UUID, also known as a pseudo random UUID, which can be generated in a browser. I suggest you skip passing in custom random generators.

    var guid: Guid = Guid.newGuid();

    What if someone gives a "GUID" to you, can you trust it is a proper Guid, no you can't you need to validate it. That can be done using the isValid method, which takes a string as in input.

    if( Guid.isValid(' f0a1f189-dae4-49f5-8846-3a17429fd52') ) { alert('Valid') };

    Then we finally also have a method to parse a string potentially containing a Guid, tryParse. The method will either return a Guid object or undefined.

    var guid: Guid = Guid.tryParse('f0a1f189-dae4-49f5-8846-3a17429fd52');

    The GuidHelpers class (@internal)

    [Update 2016-09-20] - The GuidHelpers class is marked internal and will be removed in future builds of the SharePoint Framework.

    There's also another class in SharePoint Framework for Guids, the GuidHelpers class. This class lives in the @microsoft/sp-client-preview module. It has a set of similar function to generate and validate Guids, but it does not work with Guid objects, it uses strings instead. For instance the GuidHelpers.generateGuid() returns a string. Another issue is that the GuidHelpers.isValid() does not validate Guids the same way as the Guid.isValid does (issue #201 in the SPFx Repo).

    GUID validation issues

    At the moment (drop 3 of SPFx) I would stay away from the GuidHelpers class.

    As usual there's some code samples of this to be found in this Github repo: https://github.com/wictorwilen/spfx-nuggets

    Happy GUIDing!

  • SharePoint Framework Nuggets: logging like a pro

    Tags: SharePoint Framework, SharePoint

    I guess that almost every application or solution you ever built has contained some portions of a logging mechanism. And how many of you have written your own - yup, all of you! But what about the SharePoint Framework - yes, it has built-in logging!

    SharePoint Framework Nuggets - logging like a pro

    How to log in the SharePoint Framework

    Logging is a very convenient and easy way to keep track of events happening, instead of having breakpoints, or in JavaScript even worse - alerts. The SharePoint Framework (SPFx) has as all decent frameworks a built-in logging mechanism, albeit very simple, but still yet valuable. It's contained in the @microsoft/sp-client-base module and the class is called Log. To use it in your SharePoint Framework solutions you need to import it as follows:

    import {
      Log
    } from '@microsoft/sp-client-base';

    The Log class contains four static methods for logging:

    • info
    • warn
    • error
    • verbose

    The names are exactly what you expect, the info is used for information, warn for warnings, error for errors and verbose for when you just have to much on your mind and want to spit it out.

    In the SharePoint Framework implementation all logging is done to the JavaScript console and you can see the logging using the developer tools of your favorite browser. It can take some searching to find them as SPFx spits out quite a lot of logging by itself.

    All static methods have the same signature, except the error method - they take three arguments:

    • source: the source of the logging information (max 20 characters), such as method or class name
    • message: the actual message to log (max 100 characters)
    • scope: an optional service scope (more on this one later)

    The error method takes an Error object instead of the message string, otherwise they are the same.

    This is how it could be used in client side web part:

    public render(): void {
      this.context.statusRenderer.clearError(this.domElement);
      this.context.statusRenderer.displayLoadingIndicator(this.domElement, strings.Loading);
      Log.verbose('SpFxNuggets', 'Invoking render');
    
      this._webInfoProvider.getWebInfo().then((webInfo: IWebInfo) => {
        if (this.properties.fail) {
          throw new Error('Mayday');
        }
        Log.info('SpFxNuggets', 'Service OK', this.context.serviceScope);
        this.context.statusRenderer.clearLoadingIndicator(this.domElement);
        this.context.domElement.innerHTML = `<h1>${webInfo.title}</h1>`;
    
      }).catch((err) => {
        Log.error('SpFxNuggets', err);
        this.context.statusRenderer.clearLoadingIndicator(this.domElement);
        this.context.statusRenderer.renderError(this.domElement, err);
      });
    }
    

    In the example above I use the verbose method to log that we're in the render method. Just verbose information. I also added logging to when the service returns (the promise is fulfilled) to log information that it has returned. In that info method I use the service scope of the web part, and when using the service scope, it replaces my source with the real name (JavaScript name) of the web part. Finally I log the error in the catch method. The image below shows the console output.

    Console output

    As usual you can find all the source code in this repository: https://github.com/wictorwilen/spfx-nuggets

    Happy logging!

About Wictor...

Wictor Wilén is the Nordic Digital Workplace Lead working at Avanade. Wictor has achieved the Microsoft Certified Architect (MCA) - SharePoint 2010, Microsoft Certified Solutions Master (MCSM) - SharePoint  and Microsoft Certified Master (MCM) - SharePoint 2010 certifications. He has also been awarded Microsoft Most Valuable Professional (MVP) for seven consecutive years.

And a word from our sponsors...