Google Maps Display Template – SharePoint 2013 – Part 2

As described in my previous post we will be creating a display template for Google maps. When you did not have the change to read part 1 you can do this by using the below link:

Google Maps Display Template – SharePoint 2013 – Part 1 

In part one we created the item display template and because we could not insert the JavaScript of adding markers to the map in the display template we have inserted the information in hidden inputs.

First of there is 1 important thing to notice. If you would like to use Google maps on you page you have to add a JavaScript file on you page. Within display control template you can use a specific method called “$includeScript();”. This method will include the script to the page for you. But there is a catch for as far as I could investigate if your JavaScript file doesn’t end  with “.js” it will not be included on the page. The Google maps API is a file that does not end with “.js”. The only option you have is to add the Google API file by using a script editor.

Embed Code Toolbar

Embed code

<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>

In the control template we will begin by reading out that information and placing them on the map.

To get a new control template you will need to copy an existing control template, for this example we will create a copy of the “Control_List.html” and rename it to “Control_GoogleMaps.html”.

On each hidden input we have specified an id in order to retrieve the values, the id consist of a text value and the number of the result. In the Control Display Template we can retrieve the number of the result by using the “ctx” object and the property “CurrentItemIdx”. Each marker will become an Item in the “markers” array and will be a array itself. In the iteration we also construct the information window for each marker.

var markers = new Array();

for(var i=0;i<=ctx.CurrentItemIdx;i++){
    var inputValue = document.getElementById(i+'-Location').value
    var itemArray = inputValue.split(";");

     var contentString = '<div style="height:100px;" id="content">'+ 
     '<b>' + itemArray[0] + '</b>'+
     '<hr><div id="bodyDescription" style="margin-top:2px;">'+ itemArray[3] + '</div>' +
     '<div id="bodyAddress" style="margin-top:2px;">'+ itemArray[4] + '</div>' +
     '<a href="'+ itemArray[5] + '"><div style="font-weight:bold;margin-top:5px;">Office Information</div></a></div>';

     itemArray[3] = contentString;
     markers[i] = itemArray;             
}

When we have the information of the markers we can start by placing the markers on the map. For more information on how to use the Google Maps API go here.

var mapOptions = {
     zoom: 4,
     mapTypeId: google.maps.MapTypeId.HYBRID
}

var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var bounds = new google.maps.LatLngBounds();

var infowindow = new google.maps.InfoWindow(), marker, i;
for (i = 0; i < markers.length; i++) {  
    
    var title = markers[i][0];
    var latitude = markers[i][1];
    var longitude = markers[i][2];
    var pointLatlng = new google.maps.LatLng(latitude,longitude);
    bounds.extend(pointLatlng);
        
    marker = new google.maps.Marker({
                position: pointLatlng ,
                map: map,
                title:title,
                icon:'/PublishingImages/point.png'
    });
    
    google.maps.event.addListener(marker, 'click', (function(marker, i) {
      return function() {
        infowindow.setContent(markers[i][3]);
        infowindow.open(map, marker);
      }
    })(marker, i));
}

map.fitBounds(bounds);

By saving each point within a LatLngBounds object in the example above the bounds object we can specify on the map that it has to load all point within the first view by using the method “map.fitBounds(bounds)”. 

When tying this all together you will get a control display template like below:

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

<!--[if gte mso 9]><xml>
<mso:CustomDocumentProperties>
<mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
<mso:MasterPageDescription msdt:dt="string">This is the default Maps Display Template that will show a map with all of the items. For this map you should have managed properties with the longtitude and latitude of locations. Also make sure you reference the google map api: (https://maps.googleapis.com/maps/api/js?v=3.exp)</mso:MasterPageDescription>
<mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106601</mso:ContentTypeId>
<mso:TargetControlType msdt:dt="string">;#Content Web Parts;#</mso:TargetControlType>
<mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
</mso:CustomDocumentProperties>
</xml><![endif]-->
</head>

<body>
    
    <script>
        $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
    </script>

    <div id="Control_List">
    <!--#_ 
    if (!$isNull(ctx.ClientControl) && !$isNull(ctx.ClientControl.shouldRenderControl) && !ctx.ClientControl.shouldRenderControl()){
        return "";
    }

    ctx.ListDataJSONGroupsKey = "ResultTables";
    var $noResults = Srch.ContentBySearch.getControlTemplateEncodedNoResultsMessage(ctx.ClientControl);
    var noResultsClassName = "ms-srch-result-noResults";
    _#-->
    
         _#= ctx.RenderGroups(ctx) =#_
        
    <!--#_

    function initialize() {

          var mapOptions = {
                zoom: 4,
              mapTypeId: google.maps.MapTypeId.HYBRID
          }

          var markers = new Array();
          var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
          var bounds = new google.maps.LatLngBounds();

          for(var i=0;i<=ctx.CurrentItemIdx;i++){
              var inputValue = document.getElementById(i+'-Location').value
              var itemArray = inputValue.split(";");

              var contentString = '<div style="height:100px;" id="content">'+ 
                 '<b>' + itemArray[0] + '</b>'+
                 '<hr><div id="bodyDescription" style="margin-top:2px;">'+ itemArray[3] + '</div>' +
                 '<div id="bodyAddress" style="margin-top:2px;">'+ itemArray[4] + '</div>' +
                 '<a href="'+ itemArray[5] + '"><div style="font-weight:bold;margin-top:5px;">Office Information</div></a></div>';

            itemArray[3] = contentString;
            markers[i] = itemArray;             
          }
    
          var infowindow = new google.maps.InfoWindow(), marker, i;
    
          for (i = 0; i < markers.length; i++) {  

               var title = markers[i][0];
               var latitude = markers[i][1];
               var longitude = markers[i][2];
               var pointLatlng = new google.maps.LatLng(latitude,longitude);
               bounds.extend(pointLatlng);
        
               marker = new google.maps.Marker({
                  position: pointLatlng ,
                  map: map,
                  title:title,
                  icon:'/PublishingImages/point.png'
               });
    
               google.maps.event.addListener(marker, 'click', (function(marker, i) {
                  return function() {
                     infowindow.setContent(markers[i][3]);
                     infowindow.open(map, marker);
               }
            })(marker, i));
        }
        map.fitBounds(bounds);
    }

    google.maps.event.addDomListener(window, 'load', initialize);
    _#-->

        <div id="map-canvas" style="height:500px;"></div>
    </div>
</body>
</html>

And finally when you place this all on a page it will look something like this:

Google Maps Display Template

Share Button

Google Maps Display Template – SharePoint 2013 – Part 1

As the most of you will know by now is that SharePoint 2013 has a new web part called the “Content Search” web part. This web part display’s search result by using specific display templates.

For one of our clients we were asked to display location/office information on a Google Maps card based on information they save in a list.  We first had the idea of using the new Geo Location fields that are pretty awesome. But we had a hard requirement for using Google Maps because they already had a licence for using that.

If you want more information about the Geo Location field you can read about here:

The easiest way to display this information is retrieving the results by its content type with the Content Search web part and using a Google Maps Display Template to display those results.

There are two primary types of display templates:

  • Control templates determine the overall structure of how the results are presented. Includes lists, lists with paging, and slide shows.

  • Item templates determine how each result in the set is displayed. Includes images, text, video, and other items.

The control display template defines the HTML structure for the overall layout and the item template defines the HTML structure for the item.

Combined HTML output of a control display template and item display template

If you want to read more information about display templates here is a good article:

SharePoint 2013 Design Manager display templates

In this Part we will be creating the item template. The easiest way to create a new item display template is using SharePoint Designer and create a copy of another display template.

The display templates can be found here:

All Files – _catalogs – masterpage – Display Templates – Content Web Parts

To get a new item display template you will need to copy an existing item template, for this example we will create a copy of the “Item_TwoLines.html” and rename it to “Item_GoogleMarker.html”.

When you open the file in edit mode you will see that there is a Title attribute on the top of the page. Change this title to Google Maps Marker because the item we will retrieve will represent markers on the map.

The result we will be retrieving will have the following information:

  • Title
  • Longitude
  • Latitude
  • Description
  • Address
  • Site Url (Url to a SharePoint site)

In order to get this information from search you will have to create managed properties for the specific field. In this post I will not tell you how to create those for more information you can read this post or look on MSDN for more information:

If you look further in the display template you will see the property “ManagedPropertyMapping” this will map the properties to specific variables in the file. To which property they are mapped can be changed in the tool part of the web part

image

<mso:ManagedPropertyMapping msdt:dt="string">&#39;Link URL&#39;{Link URL}:&#39;Path&#39;,&#39;Line 1&#39;{Line 1}:&#39;Title&#39;,&#39;Line 2&#39;{Line 2}:&#39;&#39;,&#39;FileExtension&#39;,&#39;SecondaryFileExtension&#39;</mso:ManagedPropertyMapping>

Each property mapping exists out of three variables separated by &#39;,&#39; and the complete properties are separated by ‘, ‘.

For example “&#39;Line 1&#39;{Line 1}:&#39;Title&#39;”:

  • The first “Line 1” will represents the value in the display template.
  • The second “{Line 1}” represents the description and is displayed in the tool part. You can change this to each value you want.
  • The third “Title” is the default managed property it is mapped to.

For our item template we will extend the property with our own values we need to be able to retrieve the right values from the search engine.

<mso:ManagedPropertyMapping msdt:dt="string">&#39;Link URL&#39;{Link URL}:&#39;Path&#39;,&#39;Line 1&#39;{Title}:&#39;Title&#39;,&#39;Line 2&#39;{Latitude}:&#39;Title&#39;,&#39;Line 3&#39;{Longtitude}:&#39;Title&#39;,&#39;Line 4&#39;{Description}:&#39;Title&#39;,&#39;Line 5&#39;{Address}:&#39;Title&#39;,&#39;Line 6&#39;{Site Url}:&#39;Title&#39;</mso:ManagedPropertyMapping>

When this is done you can change the “MasterPageDescription” property to the value of your choice.

When you go further down into the display template the values are retrieved within the div called “TwoLines”.

<!--#_
var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_2lines_");

var linkURL = $getItemValue(ctx, "Link URL");
linkURL.overrideValueRenderer($urlHtmlEncode);
var iconURL = Srch.ContentBySearch.getIconSourceFromItem(ctx.CurrentItem);

var line1 = $getItemValue(ctx, "Line 1");
var line2 = $getItemValue(ctx, "Line 2");
line1.overrideValueRenderer($contentLineText);
line2.overrideValueRenderer($contentLineText);

var containerId = encodedId + "container";
var pictureLinkId = encodedId + "pictureLink";
var pictureId = encodedId + "picture";
var dataContainerId = encodedId + "dataContainer";
var line1LinkId = encodedId + "line1Link";
var line1Id = encodedId + "line1";
var line2Id = encodedId + "line2";
_#-->

Replace this by the following code in which we get all of the values from our properties and use basically the same code the was in the original template:

<!--#_
  var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_2lines_");
  var linkURL = $getItemValue(ctx, "Link URL");
  linkURL.overrideValueRenderer($urlHtmlEncode);
  var iconURL = Srch.ContentBySearch.getIconSourceFromItem(ctx.CurrentItem);
       
  var title = $getItemValue(ctx, "Line 1");
  var latitude = $getItemValue(ctx, "Line 2");
  var longtitude = $getItemValue(ctx, "Line 3");
  var description = $getItemValue(ctx, "Line 4");
  var address = $getItemValue(ctx, "Line 5");
  var siteUrl = $getItemValue(ctx, "Line 6");
  title.overrideValueRenderer($contentLineText);
  latitude.overrideValueRenderer($contentLineText);
  longtitude.overrideValueRenderer($contentLineText);
  siteUrl.overrideValueRenderer($contentLineText);
      
  var itemId = ctx.CurrentItemIdx;
_#-->

Now that we have all of the values for a item. It is time to write the HTML we want for the display template. The easiest way is to place all of the information about the marker in a hidden input field. In the control template we will then retrieve the information and display it on the map.

The complete display template will then look like this:

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

<!--[if gte mso 9]><xml>
<mso:CustomDocumentProperties>
<mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
<mso:ManagedPropertyMapping msdt:dt="string">&#39;Link URL&#39;{Link URL}:&#39;Path&#39;,&#39;Line 1&#39;{Title}:&#39;Title&#39;,&#39;Line 2&#39;{Latitude}:&#39;Title&#39;,&#39;Line 3&#39;{Longtitude}:&#39;Title&#39;,&#39;Line 4&#39;{Description}:&#39;Title&#39;,&#39;Line 5&#39;{Address}:&#39;Title&#39;,&#39;Line 6&#39;{Site Url}:&#39;Title&#39;</mso:ManagedPropertyMapping>
<mso:MasterPageDescription msdt:dt="string">This Item Display Template will show a marker when used with the the Google Maps control.</mso:MasterPageDescription>
<mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106603</mso:ContentTypeId>
<mso:TargetControlType msdt:dt="string">;#Content Web Parts;#</mso:TargetControlType>
<mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
<mso:HtmlDesignConversionSucceeded msdt:dt="string">True</mso:HtmlDesignConversionSucceeded>
<mso:HtmlDesignStatusAndPreview msdt:dt="string"></mso:HtmlDesignStatusAndPreview>
</mso:CustomDocumentProperties>
</xml><![endif]-->
</head>

<body>
    <script>
        $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
    </script>

    <div id="TwoLines">
        <!--#_
        var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_2lines_");
        var linkURL = $getItemValue(ctx, "Link URL");
        linkURL.overrideValueRenderer($urlHtmlEncode);
        var iconURL = Srch.ContentBySearch.getIconSourceFromItem(ctx.CurrentItem);
        
        var title = $getItemValue(ctx, "Line 1");
        var latitude = $getItemValue(ctx, "Line 2");
        var longtitude = $getItemValue(ctx, "Line 3");
        var description = $getItemValue(ctx, "Line 4");
        var address = $getItemValue(ctx, "Line 5");
        var siteUrl = $getItemValue(ctx, "Line 6");
        title.overrideValueRenderer($contentLineText);
        latitude.overrideValueRenderer($contentLineText);
        longtitude.overrideValueRenderer($contentLineText);
        siteUrl.overrideValueRenderer($contentLineText);
        
        var itemId = ctx.CurrentItemIdx;
        _#-->
        <input type="hidden" id="_#= itemId =#_-Location" value="_#= title =#_;_#= latitude =#_;_#= longtitude =#_;_#= description =#_;_#= address =#_;_#= siteUrl =#_">
    </div>
</body>
</html>

One thing to notice is that we give the hidden input the ID of the result “var itemId = ctx.CurrentItemIdx;” so that we are able to retrieve all of the items in the control template by a specific ID.

In part 2 of the series I will show you how you can finish the set by creating a control template that will result in to the following:

Google Maps Card

Share Button

SharePoint Color Palette Tool (ThemeSlots)

Many off you may have seen it in presentations of Microsoft. The tool was then called ThemeSlots. Today I stumbled on the release and it is now called “SharePoint Color Palette Tool”.

This tool helps you to create your own SPColor file for SharePoint 2013 by giving you a handy interface with a preview window.

SharePoint Color Palette Tool

 

You can download the tool here:

http://www.microsoft.com/en-us/download/details.aspx?id=38182

Share Button

Active Directory Picture Synchronization

When you have stored the pictures of employees in Active Directory you would like to have the option to synchronize these pictures to their SharePoint profile.

This article describes the steps you need to take to import them into the SharePoint profiles.

 

1. Change the mapping of the Picture profile field.

Navigate to the User Profile Service Application and got to “Manage User Properties”. Find the picture property and select the edit menu item. Add a new Mapping to the “thumbnailPhoto” attribute and select “Ok” when you are done.

Before:

AD Mapping

After:

AD Mapping After

2. Perform a Full Import

On the User Profile Service Application select “Start Profile Synchronization” to start a Full Synchronization.

3. Run the command Update-SPProfilePhotoStore

This command let will create the profile pictures in the “User Photo” library in the Mysite host site collection. Run this command with the following options:

Update-SPProfilePhotoStore -MySiteHostLocation [Your Mysite Host Location] -CreateThumbnailsForImportedPhotos 1

 

4. Check the profiles for their pictures.

 

One downside to this is that you need to rerun the Update-SPProfilePhotoStore when you have a new Employee for example.

 

When you would like to try this out on your environment you can use the following PowerShell script for importing a picture in Active Directory.

#parameters
$username = "tpicture"
$picture = "C:\Pictures\msftplayground.png"

#get the active directory information
$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$root = $dom.GetDirectoryEntry()
$search = [System.DirectoryServices.DirectorySearcher]$root
$search.Filter = "(&(objectclass=user)(objectcategory=person)(samAccountName=$username))"
$result = $search.FindOne()

#if the result not equal to null
if ($result -ne $null){
    $user = $result.GetDirectoryEntry()

    #get the byte array of the picture
    [byte[]]$jpg = Get-Content $picture -encoding byte
 
    #change the active directory property
    $user.put("thumbnailPhoto",  $jpg )
    $user.setinfo()

    Write-Host $user.displayname " updated" -ForegroundColor Green
} else {
    Write-Host $username " Does not exist" -ForegroundColor Red
}

 

These steps work for SharePoint 2013 and SharePoint 2010 from CU December 2011.

Share Button

Minimize JavaScript and CSS – Web Essentials

When working on a Website or Intranet environment and you are using a lot off JavaScript and CSS files you would like to have the possibility to minimize/bundle the files.

On the web you can find a lot of solutions for this, on my last project when I was working on a website based on SharePoint 2013 (http://www.motion10.nl). I found a great extension for Visual Studio called: Web Essentials.

Key features of this extension are:

  • Bundling of JavaScript and CSS files.
  • Minimize files.

 

The extension does much more for example it also display’s a color swatch next to a specified color in you Stylesheet file Glimlach .

CSS Class

For a complete list off the features you can look on the website of Web Essentials.

The extension gives you the option to minimize a JavaScript files by clicking on it with your second mouse button. Once generated it keeps synching your changes to the minimized file (so you will keep working in the normal file).

 

Minimize files

All of the settings of the extension can be managed from the default options menu within the “Web Essentials” category that the extension adds.

Options

In my opinion this extension should be added to the list of items you install on a development environment.

Share Button

Retrieve the Friendly URL of a Publishing Page

One of my first projects with SharePoint 2013 is building a SharePoint website. For the website we had to build a functionality that display’s the URL of specific pages.

For the website we are using Managed Navigation. Displaying URLs meant we wanted to display the friendly URLS. After searching for a while with ILSpy and looking at some MSDN articles:

I found out that the friendly URL can be retrieved by first getting a list of NavigationTerm items from a SharePoint list item. When you have a NavigationTerm you can retrieve the display URL of that term using the method “GetResolvedDisplayUrl”.

In short the code to retrieve the friendly URLs will look something like this.

public List<string> GetPagesUrls() {
    
    //list for saving the urls
    List<string> retVal = new List<string>();

    //current web
    SPWeb web = SPContext.Current.Web;

    //check if the current web is a publishing weg
    if (PublishingWeb.IsPublishingWeb(web)) {

        //get the pages list id
        Guid listId = PublishingWeb.GetPagesListId(web);

        //retrieve the pages list
        SPList pagesList = web.Lists[listId];

        //itterate trough the pages
        foreach (SPListItem item in pagesList.Items) {
            //retrieve the terms used for the navigation (this can be multiple terms)
            IList<NavigationTerm> terms = TaxonomyNavigation.GetFriendlyUrlsForListItem(item, false);

            string url = string.Empty;

            //check if the pages has terms associated with it
            if (terms.Count > 0) {
                //use the GetResolvedDisplayUrl to retrieve the page friendly urls
                url = terms[0].GetResolvedDisplayUrl(string.Empty);
            } else {
                //if the page does not have any terms get the normal url
                url = item.File.Url;
            }

            retVal.Add(url);
        }
    }
    return retVal;
}
Share Button

Behold the new SharePoint Splash screen

With the new version of SharePoint 2013 that was released on MSDN last week as you can read here SharePoint 2013 has a new Installation screen to give power to the new Interface of SharePoint 2013.

SharePoint 2013 Splash

Today I will start a new fresh installation of a development machine with the following specifications:

  • Microsoft Office 2013 Professional.
  • Microsoft Office Visio 2013.
  • Microsoft Office Project 2013.
  • Visual Studio 2012.
  • Windows Server 2012.
  • SQL Server 2012.

Every interesting thing I will discover will be posted on this blog, so stay tuned!

Share Button

SharePoint 2013 Released on MSDN

12 October I wrote a post about the fact that SharePoint 2013 and Office Reached RTM. Today I noticed that it came available on MSDN. So the final version is THERE!!!!

Happy SharePointing. (I know what I will be doing today Glimlach).

sharepoint-2013

Share Button

SharePoint 2013 Reaches RTM

The Office engineering team signed off on a new Build. The build they signed off for is RTM. This means SharePoint 2013 will be able to be downloaded in less than a month from now on TechNet or MSDN.

sharepoint-2013

You can read all about it on the Office News site: http://blogs.office.com/b/office-news/archive/2012/10/11/office-reaches-rtm.aspx

Some interesting point from the news article are:

Additionally, we have a number of programs that provide business customers with early access so they can begin testing, piloting and adopting Office within their organizations:

  • We will begin rolling out new capabilities to Office 365 Enterprise customers in our next service update, starting in November through general availability.
  • Volume Licensing customers with Software Assurance will be able to download the Office 2013 applications as well as other Office products including SharePoint 2013, Lync 2013 and Exchange 2013 through the Volume Licensing Service Center by mid-November. These products will be available on the Volume Licensing price list on December 1.
  • IT professionals and developers will be able to download the final version via their TechNet or MSDN subscriptions by mid-November.

Please stay tuned for more specifics on general availability dates and other Office launch news. In the meantime, if you’d like to give the pre-release version a try, you can visit office.com/preview.

Thank you to the millions of people who have been testing early releases of the new Office. We are grateful for your support. Your invaluable feedback has helped us make the new Office the best Office ever.

Share Button