Thursday, November 23, 2017

Using ECMAScript 6 promises with JSOM

This is a small post for those of you that would be interested in using ECMAScript 6 promises with JSOM in SharePoint. A Promise represents the eventual completion (or failure) of an asynchronous operation, and its resulting value. Keep in mind that ECMAScript 6 might not be supported in all browsers. In that case, you could fall back to jQuery promises. If you want to use this in combination with EcexuteQueryAsync, you can. But you could also extend JSOM to provide a function that does this for you ...
You can use the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Extending JSOM
  SP.ClientContext.prototype.executeQuery = function (argument) {
    // Get a reference to this
    var that = this;
    // Create the promise
    var promise = new Promise(function (resolve, reject) {
      that.executeQueryAsync(
        function () { resolve(argument); }, 
        function (sender, e) { reject(e); }
      );
    });
    return promise;
  };

With that it is now easier to write something as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$(document).ready(function () {
    getUser().then(
      function (user, folder) {
        $('#message').text('Hello ' + user.get_title());
      },
      function (e) {
        alert('Failed to get user name. Error:' + e.get_message());
      }
    )
  });

  // This function prepares, loads, and then executes a SharePoint query to get the current users information
  function getUser() {
    var user = context.get_web().get_currentUser();
    context.load(user);
    return context.executeQuery(user);
  }

Tuesday, November 21, 2017

Mapping crawled properties on Site Collection level to Refinable* Managed Properties in SharePoint 2016

This blog post continues on one of my previous posts on the behavior on automatically created crawled and managed properties in SharePoint 2016. I got a question on how the PowerShell script in that blog post could help to map existing crawled properties to the Refinable* managed properties in a site collection (RefinableString00, RefinableString01, …).

First of all, to make things clear. You have to know that from SharePoint 2016 onwards, automatically generated crawled and managed properties are not stored inside the search schema database anymore. Instead, they are stored directly into the index. This is why, when using the PowerShell Cmdlets Get-SPEnterpriseSearchMetadataCrawledProperty and Get-SPEnterpriseSearchMetadataManagedProperty , you will not get any results, because those Cmdlets are looking inside the search schema, and not the index. Comparable to this, the New-* Cmdlets will also create them inside the search schema. On the other hand, the Refinable* managed properties are stored in the search schema, and configurable per site collection… (obvious, right? ;-))


Knowing these things, this implies that if you want to map a crawled property to a Refinable* managed property on site collection level, that you need to make sure the crawled property is created in the Search Schema before you map it to the existing managed property. The PowerShell I’ve used to demonstrate this is the following:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Add-PSSnapin Microsoft.SharePoint.PowerShell

# Get the Search Service Application
$ssa = Get-SPEnterpriseSearchServiceApplication -Identity "Search Service Application"

# Get Search category (origin was a site column)
$category = Get-SPEnterpriseSearchMetadataCategory -SearchApplication $ssa -Identity "SharePoint"

# Fulfillment Status
# Crawled property 1 (usable for refining): ows_Fulfillment_x0020_status
# Crawled property 2 (usable for structured search): ows_q_CHCS_Fulfillmentstatus

# Get the site
$site = Get-SPSite http://search.contoso.com

# Create crawled property
$crawledProperty = New-SPEnterpriseSearchMetadataCrawledProperty -SearchApplication $ssa -Category $category -VariantType 0 -PropSet "00130329-0000-0130-c000-000000131346" -Name "ows_Fulfillment_x0020_status" -IsNameEnum $false

# Find the existing managed property
$managedProperty = Get-SPEnterpriseSearchMetadataManagedProperty 'RefinableString00' -SearchApplication $ssa -SiteCollection $site.ID
$managedProperty.MappingsOverridden = $true;
$managedProperty.Update();

# Create the mapping
New-SPEnterpriseSearchMetadataMapping -SearchApplication $ssa -CrawledProperty $crawledProperty -ManagedProperty $managedProperty

Things to know about this is that I started from a site column with the name Fulfillment status and that this was a Choice. The site collection where I wanted to make the mapping was at http://search.contoso.com.

The crawled property is now correctly mapped, just make sure to start the crawler again, since you’ve made a change to the Search Schema.

clip_image002

Thursday, May 11, 2017

Using the Workflow Definition Language in Microsoft Flow

I’ve been playing with Microsoft PowerApps and Microsoft Flow since the first time it was introduced. And I must admit, I really love the way you can create mobile apps and flows in just a couple of minutes. I agree, once you really get in there, you bump into things that are still missing. But hey, that just means there is still room for improvement …

What I would like to show in this blog post is how you can extend your flows with a bit more advanced behavior by using the Workflow Definition Language. Actually, Microsoft Flow and Azure Logic Apps have a lot in common. Logic Apps allows you to use the Workflow Definition Language, containing a bunch of functions to give you logical behavior, functionality, parsing, conversion, … . The fact is that you can also use that Workflow Definition Language in Microsoft Flow, but it is not documented that well. Ohw, before I forget, the Workflow Definition Language from Logic Apps is described right here. Make sure you dig into that documentation to understand the functions you have at your disposition.

To illustrate how one can use it, I created a flow that will, starting from the PowerApp blog RSS create an email containing the new blog posts. Sounds like a simple one, but you need the Workflow Definition Language in order to make it a bit more fancy…

Starting with the trigger

I’ve decided to go for a trigger that runs every day, so that I can fetch the most recent blog posts every day. The setup looks as follows, still quite simple:

image

Filtering out only the “new” ones

I’m only interested in the “new” posts. Since my flow runs on a daily basis, I’m only interested in the posts that are less than a day old. Now the RSS feed items contain a field that contains the Published Date, so I want to filter on this field. In Flow, you can use the action Filter array from Data Operations to filter a collection of items. This will give you a “Simple” condition you can configure, but there is no way you can configure there: “Has to be published in the last 24 hours”. So you switch to “Advanced”, and now it’s up to the Workflow Definition Language to kick in.

image

The formula

@greater(item()?['publishDate'], adddays(utcnow(),-1))

checks whether the PublishedDate is greater than right now minus 1 day, giving you the posts created in the last 24 hours.

Preparing the HTML per blog post

Now that I have the most recent blog posts, I can start preparing them. I mean, at the end I want to send an email containing HTML to my email address. So for every item I want to create an <li> item containing an <a> anchor tag. So I want to do an Apply to each and then Concatenate some fields and text together. You can use the Compose action from the Data Operations for this.

 image

Make sure the formula is SURROUNDED WITH DOUBLE QUOTES or it will not work… The UI trims off the double quotes after save.

"@concat('<li><a href=\"', first(item()?['links']), '\">', item()?['title'], ' </a>')"

 

Only send an email if any blog post was found

This step could actually be put after the List RSS Items, but I’ve put it after the Apply to each. You can use the following:

image

Creating the email body

In the previous steps we already created the HTML for every item in the feed, now we just need to merge those items and surround it with some other HTML, just to make it a bit prettier. We can achieve this with another Compose action.

image

Again, make sure this formula is surrounded with double quotes or it will not work.

"@concat('<h1>New posts on the PowerApps blog</h1><ul>', join(outputs('Compose'),''), '</ul>')"

 

Sending the email

What remains is to send the email. Nothing more then passing on the output of the previous step as the email body. Since we constructed HTML as the body, the email can be sent as HTML too.

image

Run the flow, and there is the email:

image

Tuesday, May 9, 2017

Using Cross-Site Collection Publishing in SharePoint 2016

In this blog post I will discuss what you need to do to get cross-site collection publishing to work in SharePoint 2016. The Cross-Site Collection Publishing feature was introduced in SharePoint Server 2013 and allows you to use one or more authoring site collections to author and store content, and one or more publishing site collections to control the design of the site and to show the content. The authoring site collection contains so-called catalogs, which are lists that contain content, tagged with metadata (by means of site columns). These catalogs are indexed by the search system and made available to the publishing site collection. The publishing site collection issues queries for data that has been indexed and shows it on web pages by using Search Web Parts, like the Content Search WebPart. You brand content on the publishing site by using master pages, page layouts, and display templates.

In SharePoint 2013, cross-site collection publishing used to work out-of-the box. In SharePoint 2016, when integrating the catalog in a publishing site. No results are shown by the content search webpart that is added to the category page.

image

Although, in the properties of the webpart you will notice that the items from your catalog are in the search index. The webpart is not showing any items because it is additionally filtering the taxonomy managed property on the current and child navigation.

image

The reason that he is not showing any results in the content search webpart is caused by what I described in a previous blog post on auto generated managed properties in SharePoint 2016. Because they are not “activated” by default anymore, you have to create/activate them manually.

You can fix this taxonomy managed property in the search schema of the search service application using the following PowerShell.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ssa = Get-SPEnterpriseSearchServiceApplication -Identity "Search Service Application"
$category = Get-SPEnterpriseSearchMetadataCategory -SearchApplication $ssa -Identity "SharePoint"
$crawledProperty = New-SPEnterpriseSearchMetadataCrawledProperty -SearchApplication $ssa -Category $category -VariantType 0 -PropSet "158d7563-aeff-4dbf-bf16-4a1445f0366c" -Name "ows_taxId_Genre" -IsNameEnum $false
$managedProperty = New-SPEnterpriseSearchMetadataManagedProperty -SearchApplication $ssa -Name "owstaxIdGenre" -Type 1 -FullTextQueriable $true -Queryable $true -Retrievable $true -SafeForAnonymous $true
$managedProperty.Searchable = $true
$managedProperty.Refinable = $false
$managedProperty.Sortable = $false
$managedProperty.HasMultipleValues = $true
$managedProperty.OverrideValueOfHasMultipleValues = $true
$managedProperty.Update()
$mapping = New-SPEnterpriseSearchMetadataMapping -SearchApplication $ssa -ManagedProperty $managedproperty -CrawledProperty $crawledproperty

The PropSet ID will be “158d7563-aeff-4dbf-bf16-4a1445f0366c” since the cross-site collection category/navigation field is always a taxonomy field. To find the appropriate name for the crawled and managed property, you can search for them in the Search Schema of the Search Service Application using Central Administration. You can’t decide to rename this field because the cross-site collection configuration will be looking for this exact field. Other fields, part of your catalog, can be created with different names and used in the property mappings of the Content Search WebPart without any problem.

Don’t forget to start the crawler after having updated the crawled and/or managed properties.

Friday, May 5, 2017

Automatically created managed properties do not work OOB in SharePoint 2016

In this blog post I will discuss changes Microsoft made to the logic behind Crawled and Managed Properties in SharePoint 2016. In SharePoint 2013, crawled and managed properties were automatically created by the Search crawler. You could then immediately start using the auto generated managed properties in your search queries, they just worked out-of-the box. I admit, they did not look pritty, but in those cases, where pritty was important, you could manually create them with pritty names …

Now, since SharePoint 2016, those auto-generated managed properties still seem to be created but they are disabled. If you look at one such managed property, you will see an info message stating the following:

Automatically created managed property. ManagedPropertyOWSTEXT is an automatically created managed property. You can't see any of its existing settings or which crawled properties are mapped to it, but you can map more crawled properties to it. If you save the managed property without making any changes, all settings will be deleted.

Now what is happening here? Is this a bug in SharePoint 2016? No, it isn’t. Microsoft states that this is intended, and is inspired by how it is configured in SharePoint Online. You cannot see what the configuration is in the property, but you can change and save, overriding the “internal” settings.

But wait, be aware that when you change the settings of auto generated managed properties, those custom settings will be lost when, for some reason, you need to reset your search index…

Changes in PowerShell

Now, I’m used to provisioning my search schema with PowerShell. I never had any problems doing this in SharePoint 2013, I assumed that my PowerShell would still work in SharePoint 2016. But the CmdLets Get-SPEnterpriseSearchMetadataCrawledProperty and Get-SPEnterpriseSearchMetadataManagedProperty did not return any results. It feels like the automatically created crawled and managed properties are not there, while I can see them in the Search Service Application Search Schema.
The reason for this is that in SharePoint 2016, crawled and managed properties that were automatically created from custom columns become part of the Search Index instead of the Search Schema. And the PowerShell CmdLets mentioned above are querying those in the Search Schema.

The solution for this is to create the crawled and managed properties from within PowerShell. Assume the following crawled and corresponding managed property that you would like to activate using PowerShell:

  • MediaNameOWSTEXT (Managed property)
  • ows_q_TEXT_MediaName (Crawled property)

I used the following PowerShell to activate them:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Add-PSSnapin "Microsoft.SharePoint.PowerShell"

# Get the Search Service Application
$ssa = Get-SPEnterpriseSearchServiceApplication -Identity "Search Service Application"

# Get Search category (origin was a site column)
$category = Get-SPEnterpriseSearchMetadataCategory -SearchApplication $ssa -Identity "SharePoint"

# Create crawled property
# - PropSet:
#   - 158d7563-aeff-4dbf-bf16-4a1445f0366c - TaxonomyField
#   - fea84df6-a0fc-492c-9aa7-d28b8dcb08b3 - SPFieldMultiLineText, HtmlField, ImageField, LinkField
#   - ed280121-b677-4e2a-8fbc-0d9e2325b0a2 - SPFieldText, SPFieldChoice, SPFieldMultiChoice, SPFieldNumber, SPFieldCurrency, SPFieldDateTime, SPFieldBoolean, SPFieldUser, SPFieldUrl, SPFieldGuid
#   - 00130329-0000-0130-c000-000000131346 - other field types
$crawledProperty = New-SPEnterpriseSearchMetadataCrawledProperty -SearchApplication $ssa -Category $category -VariantType 0 -PropSet "ed280121-b677-4e2a-8fbc-0d9e2325b0a2" -Name "ows_q_TEXT_MediaName" -IsNameEnum $false

# Create managed property (Type 1 is Text, auto generated are always text)
$managedProperty = New-SPEnterpriseSearchMetadataManagedProperty -SearchApplication $ssa -Name "MediaNameOWSTEXT" -Type 1 -FullTextQueriable $true -Queryable $true -Retrievable $true 
$managedProperty.Searchable = $true
$managedProperty.Update()

# Map managed property to crawled property
$mapping = New-SPEnterpriseSearchMetadataMapping -SearchApplication $ssa -ManagedProperty $managedproperty -CrawledProperty $crawledproperty

You can ignore the warnings, this is because the parameters SearchApplication and VariantType are mandatory but ignored when called like above.