Office 365 and Kendo UI Map Control

For a recent project, I built an intranet site for a client using SharePoint Online.  One of the requirements for this project was to display the various corporate locations on a map.  The corporate locations are stored in a SharePoint List, so they can be updated by the business once we launch the site.  The list has a custom content type for Corporate Location that includes the standard Site Columns for Work Address,, Work Country,  Primary Phone, etc.  It also includes two custom columns of type Number for the location’s longitude and latitude. 

Populating the information for these columns requires some additional work.  Most organizations have the Address and Phone information for their corporate sites readily available, but probably not the exact location of the corporate site in geographical space.  The address of the location can be entered into a map service, like Bing Maps to return the corresponding latitude and longitude.  I went through the steps in getting this data in a previous blog post on SharePoint 2013 Geolocation in the section Using Geolocation.  I did the same steps for this project.  Now, I just need a map control.

I used Telerik controls for basic web development and SharePoint On Premises solution development.  In those projects, the dlls for the controls are deployed to the server in the solution package or even pre-installed on the machines.  This is a SharePoint Online project, so deploying solutions and any necessary dlls is not an option here.  We need controls that are based on a JavaScript framework as these files will download when requested by the web page and run client-side.  In addition to their server-side controls, Telerik also has a suite of JavaScript framework controls called Kendo UI.  The Professional version of these controls includes a map control, which will be perfect for our application here. 

So now that we have the data in place and our control selected, let’s wired this up.

Wiring It Up

We need to develop two components for this solution, a custom JavaScript file that runs when the page finishes loading and a snippet of code to place on the page where we need the map.

Here is the overall structure of the JavaScript file.  The file is an Immediately Invoked Function Expression (IIFE) or Self Executing Function (SEF) depending on your terminology.  It is also encapsulated in the namespace of locationMap.  I place this file in the _catalogs/masterpage in a folder named ‘js’.  This means it is only available for that site collection, which was acceptable for my project. 

"use strict";

var locationMap = window.locationMap || {};

(function () {

    function createMap() 
    $().ready(function () 


The first function is called using the jQuery document.ready function.  This anonymous function makes a REST call to SharePoint to return a collection of items in the list.  The REST call is made in the context of the authenticated user.  This means the user must have READ permissions on the list as there is no way to impersonate or authenticate with other credentials.

This collection needs a little additional processing before it is ready to pass to the map control.  This is because the map control is looking for one object containing an array of our location points, the latitude and longitude.  Our data from SharePoint has these as two separate fields in our list.  Once the data is successfully returned, I used the jQuery .each function to loop through our results and add an object named ‘location’ to the item object.  This is an array and I add the latitude and longitude values as separate items while parsing them to type Float.  The modified data.values object is then passed to the location.MapData object as a new  Calling the read function on the MapData object loads the data into the object.  Now that the data is parsed and loaded, I call the createMap function.

$().ready(function () {
    var restUrl = "/{site location}/_api/web/lists/GetByTitle('Locations')/items";
    $.getJSON(restUrl, {
        format: 'json'
    }).done(function (data) {
        $.each(data.value, function (i, item) {
            item.location = [];

        locationMap.mapData = new{ data: data.value });;


The second function is the createMap function.  This sets the options and defines the data source for the map.  I am using a template in this map to display a custom tooltip containing the location information when the user selects a pin on the map.  More on that below, but the map control needs to be aware of the template to use for the tooltip, which is what I do here in setting the marker.tooltip.template object.  For the databinding, set the dataSource property to the location.mapData object, the location property to the object in our mapData (location) and finally the titleField comes from the ‘Title’ property (this is the name of the field returned in our json object, which corresponds to the matching SPList internal field names).  The map includes an event for ‘reset’.  This event fires when the map zoom level changes and allows the points to reposition themselves based on the current zoom level of the map.  Without this, the map images will change, but the points will remain in the location of the original zoom level.
function createMap() {
        var myTemplate = kendo.template($("#TemplateHtml").html());

            layerDefaults: {
                marker: {
                    tooltip: {
                        template: myTemplate
            markerDefaults: {
                shape: "pin"
            center: [30.000000, 0.000000],
            zoom: 2,
            layers: [{
                type: "tile",
                urlTemplate: "https://#= subdomain zoom #/#= x #/#= y #.png",
                subdomains: ["a", "b", "c"],
                attribution: "&copy; <a href=''>OpenStreetMap conributors</a>"
                type: "marker",
                dataSource: locationMap.mapData,
                locationField: "location",
                titleField: "Title"
            reset: function (e) {


Now that the code is in place, I need to modify the page where this map is displayed.  The site has the Publishing feature enabled so the page is in the Pages document library.  We could include the links to css and js files in a page template, but for this project, I only need this functionality on one page, so it is not worth creating the template.  I added a Script Editor webpart to the page in the location where I need the map.  The following code snippet was then included in the webpart.
<link href= 
type="text/css" rel="stylesheet" ms-design-css-conversion="no" />
<link href= 
type="text/css" rel="stylesheet" ms-design-css-conversion="no" />
<link href= 
type="text/css" rel="stylesheet" ms-design-css-conversion="no" />
<link href= 
type="text/css" rel="stylesheet" ms-design-css-conversion="no" />
<script id="TemplateHtml">
    <h3><a href="#= marker.dataItem.LocationPageUrl#">#= marker.dataItem.Title#</a></h3>
<p style="text-align:left">
    #=marker.dataItem.WorkCity#, #=marker.dataItem.WorkState# #=marker.dataItem.WorkZip#<br/>
<p style="text-align:left">
    <b>Phone:</b> #=marker.dataItem.PrimaryNumber#<br/>
    <b>Fax:</b> #=marker.dataItem.WorkFax#
    <div class="demo-section k-header kendoMapStyle">
        <div id="map" class="kendoMapStyle"></div>
<script src= 
<script src= 
<script src="/_catalogs/masterpage/js/map/locationMap.js" 


The first section is just the links to the css required to render the control.  The next block is the script block for the html template I mentioned earlier.  i place the html I want rendered in the tooltip window.  When I made the rest service call, I included more than just the location and title information I bound to the grid.  Address, Phone Numbers and a link to the corresponding location page is also available.  To get them to display in the tooltip, I have placeholders in the html template.  They require the form #= marker.dataItem.{propertyName}#, where property name is the Internal Name of the field in the SharePoint list.
After the script block is a div where the map is placed.  It is wrapped in a parent div that provides additional styling.  Lastly, the scripts are loaded, jQuery, then Kendo and then our custom locationMap file.  The jQuery file is downloaded from the Microsoft AJAX CDN.  All of the Kendo files are pulled from a CDN that Telerik provides on Cloudfront, so i get the SSL support.  This provides faster loading of the page as this is not coming from the SharePoint Online environment and is the recommended practice where possible.

The Results

Here are a couple of images of the resulting output on the page.

Full Page with the control
image  image
Same page zoomed into the map points and the tooltip information from the SharePoint list for this location.
Hope you found this useful.  As always, send your questions or comments via twitter or use the commenting system below.

Disclaimer: I do not work for nor am I paid by Telerik.  I use their tools because they work for me, are continually innovating and provide great support when I have questions.  I mention them by name here as it is what I use for my projects.  Other controls and frameworks based on JavaScript instead of dlls to render will likely work as well.

Office 365 and Yammer Administrators

I recently completed a Philly MTC Tech Talk on Yammer Administration.  You can see the video here.  This talk was mainly about the Yammer administrative tools available in an Enterprise version of Yammer.  I did start out with some steps on enabling your Yammer Enterprise instance.  These steps are all listed on the Activation pages of site.

Life will go easier for you if you follow the steps and assign a Global Administrator who does not have a generic username prior to activation.  Yammer only assigns named users from the O365 Global Administrator group as Verified Admins on Yammer.  So the default MOD Administrator account, with the username ‘admin’ is not listed as an Admin in Yammer.  That is why you need to identify and assign one as part of the steps in provisioning the Enterprise instance. 

I have found that you can do this after the fact.  I had a previous demo instance where I did not follow the order exactly and this resulted in not being able to see the admin menu.  I removed and readded my named user to the Global Administrator group, and they now had access to the Yammer Admin area.

It is important to note that any Global Administrator you assign in O365 are automatically Verified Administrators in Yammer.  This means they have access to all data in the Yammer environment, public and private.  They can also export data, manage users and manage integrations.  You might expect the GAs to need access to the later functions, but the viewing of private data can be a concern to some networks.

Often administrators will have two accounts, their normal domain account and an admin account with the additional permissions an admin requires.  Given the current limitation with assigning administrator permissions to accounts beginning with ‘admin’, you can create Global Administrator accounts in the format of ‘admin-username’.  Creating an account this way will not add it as a Verified Administrator in Yammer.  This is not a solution, but for some networks, this may be enough.  There does need to be a better solution going forward

If you need to create Network Admins, this is done on the Admin tab of the Admin area.  Enter the email address of the person, select the appropriate pressure and click the Submit button.  Network Admins can also be promoted to Verified Admin in the same area.  Administration of Yammer Admins assigned by the Global Administrator group can only be managed in the O365 Admin area for Users and Groups.  You can see the differences in the screenshot below.


As always, put your questions below or find me on twitter.

Yammer: Accounts and External Networks

Typically, access to Yammer comes from the account associated with your company’s Yammer instance.  This is your company email address and the domain of that email address is the name of your Yammer Home Network.  For example, sign in with and your home network is  You can see this in the url as it will be

In addition to the home network, you can participate in external networks.  An external network provides the same features as your home network but includes people outside the domain.  These people can be invited by someone from the home network, or if the network is public, a user can request access.

There are number of great reasons to participate in external networks.  Microsoft and Yammer Teams use these heavily to collect feedback, provide support and share information with the community.  External networks are also a great way to connect with your customers by creating your company’s own separate external networks for project engagements or events like tradeshows and conferences.  In general, any use case where you want to securely collaborate with people outside of your corporate home network is a good reason to explore external networks.

Memberships to external networks are tied to the account used to request access to the external networks.  In the typical case, this is your home network account.  You are logged into the home network and then request access to an external network.  The approval is associated with your account and this works well.

A problem arises if you should leave  As part of mycompany’s processes, disables your domain account if they configured Directory Synchronization or if not configured, someone manually starts the disable process of your account in Yammer.  You now no longer have access to the home network,  You also no longer have access to any external networks associated with that account.

You can see this by accessing an external network directly through the url.  I recently changed companies and my previous account had access to the Office 365 IT Pro Network.  The direct url to this network is  Accessing this url with my previous company Yammer credentials gives the following notice.


So as a Yammer user, what can you do.  What I am now doing when needing access to external networks is to make the request using my personal email which just happens to be associated with my Microsoft Account, although any personal email account seems to work.  This is a good fix for those who are at a company where they are not using Yammer (at least not yet!).  I also would only use this for access to external networks that go beyond employment at any one company, like the Office 365 Technical Network or Yammer Developer Network.

There is a downside to this if you are using your corporate account to access your company’s Yammer network.  You will need to logout/login to switch between corporate and external networks as the browser can only hold one yammer connection at a time.  You can keep multiple yammer connections open by using normal and private mode for the same browser or by connecting with two different browser applications, like IE and Chrome.

What I would like to see is a change to the yammer account where it is based on my Microsoft Account so it provides access to external networks I have associated with it regardless of where I am employed.  This new yammer account also has one home network, which is tied to my corporate account.  This way if I change companies, external access remains but the home network is blocked until I enter a new set of credentials for the home network.  Just a thought.

Tweet me or post any questions you have here.

June 2014 Schedule

June is Code Camp month! Lot’s of preparation still underway.

Here is the list of events I plan to attend for June.

Thursday, June 5: Philly Game Works (Microsoft – Malvern, PA) – Have not been able to get out to this new group. They meet once a month and you can check out there site and find them on Meet up if you are interested in attending.

Tuesday, June 10: TriState SharePoint User Group (Microsoft – Malvern, PA) – I am taking over this meeting. I am presenting on SharePoint Social. I have a separate blog post that will describe the talk going up before the end of the week. In addition to doing the main talk, I am also doing the ‘On SharePoint Development’ session as well, where we will look at how to develop against the Yammer API.

Saturday and Sunday, June 21 & 22: Philly .net Code Camp (Valley Forge Casino – Valley Forge, PA) – Session is scheduled for 1:30 on Saturday, but this topic will likely change. I will be around the rest of the weekend as well. There is still room if you are interested in attending. There is a great list of Microsoft community and local speakers presenting some great content. (How did I get in here? J)

Lite month as the Philly .net team is focused on Code Camp, so no monthly meetings. They will pick up again in July.

SQL Azure – Create Login

I’ve been working on an application based on Azure tools. This has been in my instance of Azure and things have gone well. I need to bring another person into the project, but don’t want to share my database credentials. I don’t SQL Management Studio installed on this machine and the web access version inside the Azure portal does not show any way to create or modify users, at least at the time of this writing.

I do have the ability to run SQL commands in the query window. Here are the steps it took to create a new user and give access to the database.

In the Server Explorer tools in Visual Studio 2013, click the Connect to Database icon. The classic database connection box is displayed. Complete the information for the server, username and password. For the Database enter master. You need to connect to the master database to create the login and the user.

Once connected, open a new query window and run the following commands one at a time.

Create login myNewAccount with password = ‘somestringoftext’;

Create user myNewAccount for login myNewAccount ;

This creates the login on the server and creates a user in the master database. Change the connection to point to the database you need to provide access. Launch a New Query window. Don’t use the old one as it points to the master database.

Create user myNewAccount  for login myNewAccount ;

Exec sp_addrolemember 'db_owner', myNewAccount 


Again, these statements must be run separately and not in a batch. I would run the query with the statement I did not need commented out with ‘—’.

I wanted to provide db_owner permissions, but any role can be added.

Yammer Analytics – Basic Reports

New post over on Perficient’s Microsoft blog about accessing and understanding Yammer’s basic reports.  This is part 1 of 3 leading up to SPSPhilly presentation on Yammer Analytics.


Access Services in SharePoint 2013

This is a brief recap of my Philly .net Code Camp 2013.1 presentation.

Detailed information about the requirements and configuration of Access Services can be found in this TechNet wiki. It is important to note that this functionality requires the following versions of the related applications.

  • Access 2013
  • SharePoint 2013 Enterprise Edition
  • SQL Server 2012

Access 2013 still provides the classic ‘Desktop’ databases we built over the past 20 years. The new version also includes a ‘Web App’ database which creates a SharePoint hosted app for the web front-end and a SQL Server database for storing the content. This is a huge change from the 2010 version of Access Services were the tables were converted to SharePoint Lists. SPLists provide a better scalability story than the traditional desktop database, but still have the list throttling limitations inherent in a SP List. Moving the table content to SQL Server addresses both the scalability and size issue by providing a true relational database engine to support the data. The use of SQL Server is completely transparent to the user as this communication is handled by Access Services communicating directly to SQL Server.

Access 2013 also provides some templates to get the process started. Many of the templates come in both desktop and web app versions. It is important to note as there is no switching from one version to the other after the template is chosen. There is also no upgrade path from previous versions of Access databases into the Web App database. This may be one reason why SharePoint 2013 still contains a legacy service for hosting Access 2010 web databases that come over in a content migration. Also desktop databases can contain VBA code behinds. So while the Access database artifacts all have complementary SQL Server objects, there is no analogous object for VBA code in Access 2013 Web Apps. This presents one of the limitations to the current implementation that changes are more configuration than customization via code. I think this is an area that will be improved in further releases, most likely by allowing javascript and CSS changes as opposed to c#/VB code behind files.

Let’s talk about the SQL Server component. As mentioned this must be a SQL Server 2012 database. It is also recommended that Access Services use a SQL Server instance separate from the SharePoint Farm instance. This will provide isolation for your Access Web App databases in case you need to connect to these databases through other means. A packaged web app deployed to the SharePoint Apps site creates an instance specific database each time the app is added to a site so these databases can quickly multiply. The Configuration Wizard will use the existing SharePoint Farm Database as the location for storing Access Web App databases. This can be managed in the Access Services page under Manage Service Applications in Central Admin. It is important to note that the databases created for the Access Web Apps are not included in any of the SharePoint Farm backups. These databases must be included in your disaster recovery scenarios for your environment to ensure this data is protected.

On the SharePoint side, the custom web app, once deployed to the app store and added to a site, takes on the properties of the hosted site. SharePoint permissions work on the Access Web App just like they would on a SharePoint list. Any site specific branding is also applied when viewing the Access Web App.

There is more coming on this subject as we had some good discussion and interesting questions raised during the session.


Get every new post delivered to your Inbox.

Join 335 other followers

%d bloggers like this: