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
Set-PnPTenantSite -Url https://contoso.sharepoint.com/teams/Communication201 -Sharing ExternalUserSharingOnly

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.

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;
}