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

Friday, July 7, 2017

A first look at the layout options in Communication Sites

Note: This article is based on the preview of Communication Sites

Microsoft announced the preview of Communication Sites in first release a little over a week ago. At first I was expecting more, but after playing more with it, it’s actually a pretty good preview when it comes to content layout. And with the new Hero web part, which let’s you manually highlight content/links, creating a nice looking landing page is much easier than before.

image

The main difference between a Communication site and a Modern site as far as I can tell are that a comm sites have no left navigation, and you have the option to enable commenting on the pages you create.

Thursday, July 6, 2017

Getting URL query parameters when working in the SharePoint Framework

Image result for upper lower case p

Fetching values from the query string of the URL is something developers are constantly doing, as in this sample URL where I have two parameters, mvp and status.

https://madcow.sharepoint.com/?mvp=mikael&status=awesome

What you can use is the URL object in JavaScript which is still in a proposed spec.

The URL object is not fully supported by all browsers yet (looking at you Microsoft), but fortunately SPFx provides a polyfill for the URL object so it will work fine in IE10 as well.

There is however a small issue. Currently SPFx uses TypeScript v2.2.2, which access the query parameters with searchparams (lowercase p) instead of searchParams (uppercase p). This means that to avoid an error in VSCode or when building you need to use the lowercase p version – but this breaks in browsers which expect the uppercase p version *sigh*.

TypeScript v2.3.4, for example, support the uppercase p version just fine. So until SPFx bumps the TypeScript version to something supporting the uppercase p version, a workaround is to use the any keyword in TypeScript at the cost of loosing intellisense, but it works.

const url : any = new URL(window.location.href);
const mvp = url.searchParams.get("mvp");
const awesome = url.searchParams.get("awesome");

This way you won’t get errors when building your SPFx solution, nor in any browsers. Happy query parametering – or write your own parser (or as Waldek Mastykarz said when chatting on this, use left-pad :)

Wednesday, July 5, 2017

Getting signalR to work with the SharePoint framework

image

I’m creating a web part which will talk to a signalR back-end service for some document archiving with SharePoint. signalR on the client requires jQuery, which is the tricky part. I tried a few things without getting it to work, but turns out it’s not too hard to get working when you have Waldek Mastykarz on speed dial, so this post is mostly his work :)

First off you need to include signalR, and I picked the un-official npm package ms-signalr-client. Add it with:

npm i ms-signalr-client -save

I had already read Waldek’s post about how to include jQuery as an external reference, but it didn’t “just” work. This was the point where I called up Waldek.

By using Rencore’s script checker, we found out that signalR was a non-module, and that it requires jQuery to be available on the $ sign. To make this work I had to change the globalName reference in config.json of my SPFx project to $ instead of jQuery.

"externals": {
    "jquery": {
      "path": "https://code.jquery.com/jquery-2.1.1.min.js",
      "globalName": "$"
    }
}

and in my .tsx file I had to add the following imports:

import * as $ from 'jquery';
import 'ms-signalr-client';

I could now instantiate signalR as easy as pie in my code.

let connection = $.hubConnection(https://myserver/, { useDefaultPath: false });