Question

I'm trying to build a site map for my site collections, let's call it for the purpose of this topic a secondary navigation menu.

Think of the "You are here" type of map, with the structure of a table of content, root site is 1, sub sites are 1.1, a sub site of the sub site 1.1.1, like this:

Root (Site collection)
|
- Subsite 1
- - Subsite 1.1
- - - Subsite 1.1.1
|
- Subsite 2
- - Subsite 2.1
- - - Subsite 2.1.1
- - - Subsite 2.1.2

This can be achieved easily with a web part called Table of Contents, exactly as intended with the one problem: it is based on CQWP and it slows to a crawl -pun intended-. In Sharepoint On Premises it would probably be almost imperceptible but on SPO it can make waiting for it to load a modem connection from the 90s.

Is there a way to build a table of contents using CSWP?

Was it helpful?

Solution

I built this using a CSWP and a custom Display Template. In CSWP you have to restrict the search to only return Sites (Query string = "contentclass:STS_Web" ) and sort the results either by Url or SPSiteUrl.

Then you create a custom Display Template (simplest way is to copy an modify the file Item_Site.html in MasterPage Gallery -> Display Templates -> Search.) Let's call it Item_Site_Nav.html. Then edit the Item_Site_Nav.html-list item's properties and add 'UrlDepth':UrlDepth' to the Managed Properties. And lastly set this Display Template in the CSWP Display Settings.

My modified Item_Site_Nav.html looks like this. I think it could be stripped some more.

<html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"> 
<head>
<title>Website for Navigation</title>

<!--[if gte mso 9]><xml>
<mso:CustomDocumentProperties>
<mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
<mso:MasterPageDescription msdt:dt="string">Website for navigation.</mso:MasterPageDescription>
<mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106603</mso:ContentTypeId>
<mso:TargetControlType msdt:dt="string">;#SearchResults;#</mso:TargetControlType>
<mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
<mso:ManagedPropertyMapping msdt:dt="string">&#39;Title&#39;:&#39;Title&#39;,&#39;Path&#39;:&#39;Path&#39;,&#39;SiteLogo&#39;:&#39;SiteLogo&#39;,&#39;SiteDescription&#39;:&#39;SiteDescription&#39;,&#39;CollapsingStatus&#39;:&#39;CollapsingStatus&#39;,&#39;DocId&#39;:&#39;DocId&#39;,&#39;HitHighlightedProperties&#39;:&#39;HitHighlightedProperties&#39;,&#39;FileExtension&#39;:&#39;FileExtension&#39;,&#39;ViewsLifeTime&#39;:&#39;ViewsLifeTime&#39;,&#39;deeplinks&#39;:&#39;deeplinks&#39;,&#39;importance&#39;:&#39;importance&#39;,&#39;FileType&#39;:&#39;FileType&#39;,&#39;UrlDepth&#39;:&#39;UrlDepth&#39;</mso:ManagedPropertyMapping>
<mso:CrawlerXSLFile msdt:dt="string"></mso:CrawlerXSLFile>
<mso:HtmlDesignPreviewUrl msdt:dt="string"></mso:HtmlDesignPreviewUrl>
<mso:HtmlDesignConversionSucceeded msdt:dt="string">True</mso:HtmlDesignConversionSucceeded>
<mso:HtmlDesignStatusAndPreview msdt:dt="string">http://spoint.netze-duisburg.loc/_catalogs/masterpage/Display Templates/Search/Item_SiteIndentation.html, Konvertierung erfolgreich.</mso:HtmlDesignStatusAndPreview>
</mso:CustomDocumentProperties>
</xml><![endif]-->
</head>
<body>
    <div id="Item_Site">
<!--#_ 
        if(!$isNull(ctx.CurrentItem) && !$isNull(ctx.ClientControl)){
          var urlDepth = ctx.CurrentItem.UrlDepth;
          var indentation = ''; //'<span>-&emsp;</span>'.repeat(urlDepth);
          for (i=0; i<urlDepth; i++) {
            indentation = indentation + "<span>&emsp;</span>";
          }
_#-->
      <div>_#= indentation =#_<a href="_#= ctx.CurrentItem.Path =#_">_#= ctx.CurrentItem.Title =#_</a></div>

<!--#_ 
        } 
_#-->
    </div>
</body>
</html>

The indentation span-element can be replaced with whatever CSS/html you like.

Don't forget to publish your custom display template.

Cheers, Ben

OTHER TIPS

I'm not sure about content search webpart, but I've done something similar using javascript (CSOM). With this approach, you can use browser storage (ie. session storage, cookies, etc.) to store the navigation data so repeat loads require no overhead on either the server nor the client. It helps with performance issues assuming more than 1 page load on average.

Here's the code to get immediate subsites... you could expand this further to get the full navigation structure. It's security trimmed as well.

            var ctx = new SP.ClientContext.get_current();
            var siteData = ctx.get_web().getSubwebsForCurrentUser();
            ctx.load(siteData);
            ctx.executeQueryAsync(function (sender, args) {
                var enumerator = siteData.getEnumerator();
                var sites = [];
                while (enumerator.moveNext()) {
                    var site = enumerator.get_current();
                    sites.push({ title: site.get_title(), url: site.get_url(), description: site.get_description() });
                }
                //do something with sites, jquery up some divs or bind with angular, knockout, etc.
            }, function (sender, args) {
                console.log(args.get_message());
            });
Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top