Wednesday, April 16, 2014

Explaining SQL alias to a dummy

“If I wear a Donald Duck mask you think you are talking to Donald Duck. Then I give the mask to someone else, and you still think you are talking to Donald Duck”

image

“As long as you’re talking to Donald Duck, you get Donald Duck, which is all that matters”

Tuesday, April 15, 2014

I'm Speaking at the European SharePoint Conference 2014

espc-badgeThe European SharePoint Conference is less than three weeks away and I’m super excited to be part of such an exceptional line up. The conference will take place in Barcelona, Spain from the 5-8th May 2014 and is Europe’s largest SharePoint event bringing you great sessions and the latest innovations from Vegas. This will be my first trip ever to Barcelona, so it better be good!

Browse through the superb conference program including 110 sessions, keynotes, and tutorials, including topics covering the latest news from SPC14 including what's new with SharePoint 2013 SP1 - Office Graph/Oslo - new Office 365 REST APIs - Access Apps - Cloud Business Apps.

I will be conducting a session on “Rock Your Office 365 (SharePoint Online) Search with 13 Easy Tune-Ups”, so much more end-user oriented compared to my SPC14 talk on relevance tuning.

 

Page hanging on load in SharePoint–SOLVED!

I recently had an issue which manifested itself specifically in the date picker in SharePoint 2010 where it took around 3 seconds for the picker to show after it was clicked. The error occurred regardless of browser (Chrome, IE10, IE11) and regardless of operating system (Windows7/8, OSX) – but somehow it did not show on my Surface 2.

I tried to investigate the issue with Fiddler and network monitor in the browsers dev tools, but didn’t see anything which made me trigger. I then posted the questions among my peers and Ed Musters reminded me again of using Fiddler, which was a week ago. Today I had the courage to revisit the issue and start investigating.

What did I find… a request from my page hanging on load. The page tried to load a resource from https://_layouts/Folder/somestyles.css.

Hmmm… no host name in that request. I next examined the html of the page and found the following line:
<link rel="stylesheet" type="text/css" href="//_layouts/Folder/somestyles.css"/>

Notice the double slashes in the start of the link.

Ladies and gentlemen, THOSE SLASHES ARE THE CULPRIT!!

Note: // is ok in scenarios where it's followed by a resolvable DNS hostname, where // is translated by the browser to either http:// or https:// depending on which protocol your page is served from.

The resource had been included via setting the AlternateCssUrl property, and the failing code looked as follows:

cssUrl = string.Format("{0}/{1}", site.RootWeb.ServerRelativeUrl, cssUrl.TrimStart(new[] { '/' }));

What happens is that the server relative URL of the root web is “/” and the string format part adds another slash. The code should have checked if the root web was on a path named site collection or not (length > 1), or trim/replace double slashes in the line after.

Changing  the URL to start with just one slash did the trick and the date picker now loads before you can say CODE-REVIEW.

Monday, April 14, 2014

Workaround to show Contact items in People Search

The good thing about community is that it spawns off a lot of ideas for blog posts. The idea for this post I got after my search buddy Matthew McDermott wondered if there is a way to blend results from the Local SharePoint search index with the Local People search index. And his question was spawned from this SO question, asked by Alvaro Monton.

Technically both SharePoint content and People search are in the same search engine, but they have been separated and use different search schemas and ranking.

[Update - 2014-04-15]
In SharePoint 2013 on-premises it's still possible (noticed by Alvaro), but you have to do the other way around. Choose "SharePoint Search Results" as the type, and include people results explicitly. Something like:
{?(({searchTerms}) ContentClass=urn:content-class:SPSPeople)} {?OR (({searchTerms}) spcontenttype:contact)}

[Update – 2014-04-14]
It is indeed possible, but only in SharePoint Online it seems. I tried in SPO on the tenant level and did the following. Create a new result source, pick “People Search Results” as the type, and change the query to the following:
{?({searchTerms})} {?OR (({searchTerms}) spcontenttype:contact)}
You still need to do work on the control and possibly display template to get it to show correct.
If you are on-premises, you might have to resort to the blend workaround.


What doesn’t work is to create a new Result Source which searches both, as you have to pick “SharePoint Search Results” or “People Search Results” as your index source.

What does work, is creating a query rule for people search which add a result block with for example Contact items.

Disclaimer: The following sample is not production ready, but show the concept of blending results.
Topics covered:
  • Create a new result type
  • Create a query rule with routing
  • Create a custom control template to merge results

Friday, April 4, 2014

S15E02: KQL–On Kryptonite

This is the second episode my series “SharePoint Search Queries Explained - The Series”. See the intro post for links to all episodes.

In episode one I covered the basic query operators of KQL. In this episode I will cover the advanced ones, as well as show samples of how it can all be put together in useful scenarios together with some nice to know managed properties.

Content Search Web Part “Hello World” using Query Rules

Helge Solheim did a post on how to do “Hello World” using the CSWP, where he retrieves a document with the title of “Hello World”.

Figured I’d do the same, using a Query Rule instead Smile

I first tried to create a rule which triggers on a search term, and then configuring the CBWS to execute that query. But turns out if you do this, the rule will not fire for some reason.

Hence it got a bit more complicated.

Thursday, March 27, 2014

S15E01: KQL – The Basics

This is the first episode in my series “SharePoint Search Queries Explained - The Series”. See the intro post for links to all episodes.

SharePoint 2013 includes two query languages which can be used to formulate your search queries. The Keyword Query Language (KQL) and the FAST Query Language (FQL). KQL is the topic for episode 1 and 2, and is the language you will mostly use when writing search queries, and is aimed at end-users. FQL has some extended capabilities over KQL, but you will usually solve your queries using KQL. FQL is also trickier to execute using the UI/CSOM/REST, but more on that in episode 10.

Wednesday, March 26, 2014

RESTful way to retrieve the Search Center URL

On one of my daily Fiddler adventures I stumbled upon a REST end-point I hadn’t seen before in SharePoint Online (Does not currently work on-premises with 2013 sp1, but hoping it will arrive in an update).

/_api/search/searchcenterurl

The end-point is not documented or mentioned on MSDN, but is easy to use. You call it with a GET and get something like the following back in JSON

{"d":{"searchcenterurl":https://contoso.sharepoint.com/search/Pages}}

If you’re using jQuery you can get the search center url for the current site using the following code:

jQuery.ajax({
url: _spPageContextInfo.webServerRelativeUrl+"/_api/search/searchcenterurl",
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
})
.done(function (data) {
alert(data.d.searchcenterurl);
});


A use case could be that you have a page with some tags displayed, and when clicking on the tags you want to execute a search from the search center on the clicked tag. By tacking on Results.aspx?k=<your query> to the returned URL you can easily redirect the user to the result page of your choice with a predefined query.

Tuesday, March 25, 2014

Using Tags as a Refiner

In my recent talk “Rock your SharePoint Online Search With 13 Easy Tune-Ups”, which I presented at SharePoint Saturday in Stockholm and will present again at the European SharePoint Conference in Barcelona, I talked about removing unwanted/unused refiners.

On that note I got contacted by Paul Hunt, who wondered about the Tags refiner which was out of the box in SharePoint 2010, but is not in 2013. In my opinion this refiner is seldom used. This could because it appears “below the fold” or under the screen you initially see, or that people don’t have a concept of tagging.

Either way, Paul has customers who found it very useful and who are used to tagging. The good news is that it’s very easy to add it back.

If you edit your search result page, then edit the Refinement Web Part, click Choose Refiners, add Tags to your list of refiners, and you should be all set.

image

Monday, March 24, 2014

Caveats when using Summary Link Web Part in a Web Template

My scenario was that the Summary Link Web Part had been used on a site which was to be saved as a site template using the UI (Save site as template). Saving the template worked fine, but once you created a new site based on the template you got the infamous “List does not exist” error.

The reason for the error is that the SummaryLinkWebPart class inherits the DataFormWebPart class, which has a property called ListId. If the page which hosts the Summary Links Web Part is located in for example SitePages, then the id will point to that list. Once you save the template, this id will come along, and will of course not exist in the new site.

When it comes to the Summary Link Web Part it really don’t need a reference to a list, as it’s storing all the data inside the web part itself. To fix up the template site before saving it I wrote the following PowerShell script.

function FixWebpart($wpm) {
$count = $wpm.WebParts.Count-1
ForEach ($number in 1..$count )
{
$webpart = $wpm.WebParts[$number]
# Check if it's a summary link web part
if( $webpart.PsObject.TypeNames[0] -eq 'Microsoft.SharePoint.Publishing.WebControls.SummaryLinkWebPart' ) {
write-host "Fixing listid - " + $webpart.Title + " : " + $webpart.ID
# Set the id to a blank GUID
$webpart.ListId = [guid]::Parse("00000000-0000-0000-0000-000000000000")
$wpm.SaveChanges($webpart)
}
}
}

#Get a reference to the template site
$web = get-spweb https://intranet.contoso.com/sites/template
$list = $web.Lists.TryGetList("Site Pages")

foreach($item in $list.Items) {
$wpm = $web.GetLimitedWebPartManager($item.Url, [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
FixWebpart($wpm)
}