Monday, December 16, 2013

How to get coordinates for geolocation field (JavaScript solution)

We all probably now know about the new cool feature in SharePoint - geolocation field, a new field that allows you to specify a location for a SharePoint item.

The problem with this field is it is little complicated to set its value. Out of the box it allows you to specify its value by using your current location or by manually setting longitude and latitude, probably something you don't know. So to enable users to specify the location in a more friendly way you had to create a custom field that extends the geolocation field. This is not an option in SharePoint Online.

In this post I will show  how you can provide your users with a way to search for a location in SharePoint Online by simply using a little bit of JavaScript and Microsoft.Maps API, which contains the core types of the Bing Maps AJAX Control. To learn more about this API be sure to visit Microsoft.Maps API Reference and Maps AJAX control examples, for me this is a really powerful and easy to use way to create geolocation solutions on web.

For start I will create a list for my geolocation field. It is a simple list with a Title, Description and a geolocation field I called Location. The list will be called "Wonders of the World", so you can probably guess what it will contain. If you need help for adding a geolocation field to the list be sure to check my Adding geolocation field to SharePoint Online post.

If you try to add a new item to this list  you will get a form that looks like this:

So here you can see how your location field looks like by default. We'll first need to change it so we can more easily modify it. To do this we will need SharePoint Designer.

So open your SP Online site in your SP Designer, navigate to Lists and Libraries and select the list with your location field. Now we will create a custom NewForm (in this post I will show you this customization on a NewForm , but this  also applies on the EditForm).
At the Forms section in Designer select New and in the new list form wizard select New item form as a type of a form to create and check the box to set it as a default form.





Now when you go back to your list and try to add an item, your form will look like this:

This is great because now on the form I have two input elements for longitude and latitude that I can easily access from JavaScript.

We will need to find the id's for this input elements. You can do this by using Developer Tools in Internet Explorer, press F12 to start the developer tools and use the DOM explorer to examine those input elements, they will look something like this:

So once you locate the input elements write down their id values, we are going to need them later.

Now let's get back to our SharePoint Designer and in it examine our new CustomNewForm.  Search in it  for you location field, you should find it in a row that looks like this:



Under this row we will insert our own HTML code that we will need in our custom search for locations. So insert :
     
     

Save the change and look at your form from SharePoint, it should look like this

You can see we have added an input element where users can insert search terms or  an address for a location. We have also added a div element which will be a placeholder for displaying a location on a map.
And now for the JavaScript. Go back to the CustomNewForm in your SharePoint Designer. Locate the following line <asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server"> and  immediately under that line add the following Javascript code (You will probably have to be in Advanced mode). Be sure to change the values for longitude and latitude input field ID, and for the Bing maps key in the places I specified. Also in production you will probably want to extract this script to a separate file, but for demo purposes I am adding it directly on the page. Here is the code:



Save the page and you are ready to test it, go back to your list and start creating a new item. Enter a search term for your location and press the search button we created. If everything is OK you will get something like this:




You get a representation of your location on a Bing map and the longitude and latitude fields for your geolocation field are set, so when you save your item you will also save the location you see. Also the you can drag the pushpin  (the DragHandler function in the script) so you can simply adjust the position.

I won't go into details of my code as it is pretty straightforward, but if there are any questions feel free to contact me.



Wednesday, November 13, 2013

Missing items from virtual directories when upgrading solutions to SP2013

Recently I was working on a SharePoint migration from SP2010 to SP2013. The migration involved several custom solutions that contained various items ranging from custom web parts to web services.

The process for upgrading those solutions was pretty straightforward except for one problem that involved my custom field types. After successfully rebuilding my solution to work on SP2013 and deploying them to my test environment, when I tried to add them to my test list I was greeted with an "Cannot find ..." error for my FieldEditor control. After assuring that there was nothing wrong with my code, I inspected the field definition XML file.
In it I found that the missing field editor control is referenced like this:
....
<Field Name="FieldEditorUserControl">~/_CONTROLTEMPLATES/DatabaseLookup/CustomFieldEditor.ascx</Field>
....

And there is the problem, in SharePoint 2013 there is a small change in a way you are accessing your virtual directories (folders in 15 hive like _layouts and _controltemplates) described in this article:
http://msdn.microsoft.com/en-us/library/dn296464.aspx

"...This new system has implications for you as you develop SharePoint 2013 solutions and apps, particularly when you are using the SharePoint 2010 SDK. In any app for SharePoint (which only run in SharePoint 2013 mode) and in any SharePoint solution that you know is only going to be used in site collections that run in SharePoint 2013 mode, you need to add the "/15" yourself to all the _layouts and _controltemplates virtual paths you create in your solution/app..."

After changing my xml file to get my FieldEditorUserControl  in /_CONTROLTEMPLATES/15/... all was good. Later on I had to also adjust the calls from other systems to custom web services deployed to Sharepoint layout folder, because they were no longer found at ../_layouts/webservice.asmx, they were at
../_layouts/15/webservice.asmx.

So if you are migrating your custom solutions from SP2010 and get an error that says it cannot find some file and you are sure that it is there, check the way you are referencing those files and see if they are compliant with this new system.

Friday, August 16, 2013

How to pass parameters between your App parts?

With SharePoint web parts developers had an option to make them communicate with each other using standardized set of interfaces called connection interfaces that allowed Web Parts to exchange information with each other at run time. (Creating a Connectable Web Part)

In the new SharePoint App model, web parts are replaced with app parts. The problem is they don't have an equivalent to Web Part Connections. So how can you pass parameters from one part to another?

To answer this you have to consider several things:
  • App parts are basically small applications that are presented in your SharePoint site through iframes
  • each app is running in a separate domain and so are their app parts. Because of this any direct manipulation through some javascript code is out of the question because it would mean cross-domain scripting and all you would get is an "access denied" message
Thankfully there is a solution for this that is provided through HTML5 and the postMessage method.
This method provides a way for communicating between browsing contents in HTML documents.

To demonstrate this I will use Napa tools with my SharePoint Online to create two App parts, one to send a message and the other one to receive it.

Although theoretically you can achieve app parts to communicate directly with one another I found it a much better choice to add additional code to the page where my app parts are located (using script editor for example) and make the communication go through there.
                     
So first lets send a message from one of our app parts.

Using Napa tools I will create a new App (I named it SenderApp). Add this following JavaScript function to its ClientWebPart.aspx page.

function sayHello()
  {
   var appMsg = {
      'msg':'Hello from sender app part'      
  };
  
  window.parent.postMessage(appMsg, "*");
  }


In this function we have created an object appMsg that contains only a variable called msg with some text. Similarly you can define your own  app object that you wish to share with other apps or SharePoint. As we know our app part will run in an iframe on our SharePoint site so we can use  window.parent.postMessage to post that object to the SharePoint page on which we have placed our app part.

To execute my function I added a button to my app part that calls it on click.
<button id="bttPost" onclick="sayHello()">Say Hello</button>

This is the end result for my SenderApp part:



Now for the app part that will receive this message. Again using Napa tools I have created another app (I called it ReceiverApp). In this app we will add a listener that will wait for a message that will come from other sources using this following script.

window.addEventListener("message", receiveMessageInApp, false);
function receiveMessageInApp(event)
{   
divMsg=document.getElementById('divMessages');
divMsg.innerHTML= divMsg.innerHTML+event.data+'<br/>';
}
So to catch the message from our app part we will use window.addEventListener that looks out for incoming messages and calls receiveMessageInApp function when it picks one up. It would be a good practice to send some additional information in your post message object, so you can check which part (if more of them sends messages) posted a message. Also you can use event.origin to see that.
receiveMessageInApp  method is used to display the data we will receive through messages.
This is how my part looks in the end:



Now you can package both of your apps, deploy them to your SharePoint site and add the app parts to a page. On the same page add a script editor. In that editor we will catch the message sent from our sender app part, process the message and forward it to our receiver app. Here is how my page looks like after I add all the parts:


Add the following script to your script editor to process messages from the sender app and forward them to your receiver part.
<script>

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event){
procesHello(event.data.msg);
}

function procesHello(msgData){
var iFrame = document.getElementsByTagName("iframe")[1];   
iFrame.contentWindow.postMessage(msgData,"*");
}

</script>
So in my script editor I am first listening for a message to arrive and then I am searching in my page for the second iframe element (or the second app part) and forwarding data of that message to its window(you will probably want to check for the id or something else that defines the iframe of the app part to which you wish to send your data to). If everything is correct, when you press Say Hello button, you should see that the message was processed in the ReceiverApp part.



From this simple example we see how we can achieve communication between app parts using postMessage
So one app part sends a message to the parent page, the page receives the message and sends it of to another app part if needed.

The only limitation is it will not work on older browsers since this is a HTML5 feature but other then that this is a very effective way to achieve app parts communication.

Friday, May 31, 2013

How to get coordinates for geolocation field (No code solution)

Geolocation field is a great addition to SharePoint that enables us to easily give some location meaning to our data.

When we add a geolocation field to our list and want to set our location data we will see that we have two options for doing this. If our environment allows it we can use "Use my location" option or we can go with the "Specify location" option.


When we select that we want to specify location, we will get this dialog:


In order to add a specific location to our list we need to know its exact coordinates and to get them we will probably have to go to Bing Maps and search for them. It would be great if we could simple enter some search values for our location and store coordinates from search results automatically, but there is nothing like that out of the box.

So it may seem that our only option is custom development but there is a nice workaround. I will show you how you can achieve this using a SharePoint list, a new workflow feature for calling web services and Bing Maps. I will not go into details so it would be good if you would have some understanding of REST services and the new Call HTTP service action in SharePoint workflows.

First we will need a SharePoint list with a geolocation field (if you need help adding a geolocation field to your list, check out my Adding geolocation field to SharePoint Online post), I will call mine Geolocation. Also we will add one additional column to store information about our locations, I have added a text column named Place.

The idea is that when user enters location information into the Place column, Geolocation field is automatically populated with coordinates.

To achieve this we will need to create a workflow. This workflow will use Call HTTP service action to call Bing Maps Rest services to get coordinates.

This is how the workflow looks like in the end:



I will go through each step to show you how it works.

As we can see, it has two stages. In the first stage we use Bing Maps services in order to get coordinates and in second part we use them to set values in our location field.

Here is how we get our location coordinates:

1. First we need to build up the request header. For this we need to create a dictionary with two properties Accept and Content-Type, both with values application/json; odata=verbose



Save the dictionary as BingRequestHeader.

2. Bing Maps provide us with REST services we can leverage to get geolocation data. You can read more about it here Bing Maps REST Services. We will use one of this services to get coordinates based on values user inputs into the Place column.
Add the Call HTTP service action to your workflow. For the web address of the service we will use URL that looks like this:

http://dev.virtualearth.net/REST/v1/Locations?q= <lookup to your place column>&o=json&key=<Your BING Maps key>

We will set the HTTP method of the call to HTTP GET.




Next we need to set the request header of our call to the previously created BingRequestHeader dictionary. To do this, right click on the action and select properties. In the properties window you will find a place where you can add your dictionary to request headers.

Create another dictionary called GeolocationResponse. We will use this dictionary to store the response content from our call, so in the Call HTTP service method set the ResponseContent to this new variable.

3. In the response from Bing Maps service we will get a json object that contains our coordinates. We have stored this data into our GeolocationResponse dictionary. In order to get longitude and latitude we need to search this dictionary and get values from it. For this we will use Get an Item from a Dictionary action. For the path of the item  enter :
resourceSets(0)/resources(0)/point/coordinates(0)



This will give you latitude. Set the output to a new variable that we will call Latitude

4. We will get longitude the similar way like we did latitude, we will just change the path of the item  to:
resourceSets(0)/resources(0)/point/coordinates(1)
Set the output to a new variable that we will call Longitude.


With these steps we will get our coordinates from the Bing Maps. Unfortunately, there is no action that allows us to simply set our geolocation field to those coordinates from the workflow. This is what the second stage of the workflow is for. In the second stage we will also use Call HTTP service action, but in this case we will use it to post data to our geolocation field using SharePoint REST Services.

5. First we need to set up our request header as we did when we were calling Bing Maps service. Create a new dictionary SharepointRequestHeader with the same values as BingRequestHeader.

6. To be able to post data into SharePoint list we will have to create a valid object that SharePoint can process. To create this object we will need to build several dictionaries.
Since we will need to create several dictionaries I will specify its properties in this form (name| type | value). First we will build a dictionary called metadata. It contains this property :
type | String | SP.Data.LocationsListItem (note that this will depend on the title of your list)



7. Build a dictionary called GeolocationFieldMetadata with
type | String | SP.FieldGeolocationValue



8. Build a dictionary called GeolocationFieldData. It should contain following properties:

__metadata | Dictionary | (Lookup to previuosly created GeolocationFieldMetadata dictionary)
Altitude       | Number     | 0
Latitude      | Number     | (Lookup to variable Latitude)
Longitude   Number     | (Lookup to variable Longitude)
Measure      | Number     | 0



9.  Build a dictionary named body with following properties:

__metadata | Dictionary  | (Lookup to previuosly created metadata dictionary)
Geolocation | Dictionary  | (Lookup to previuosly created GeolocationFieldData dictionary)



10.  We are now ready to update our field through Call HTTP service action. To update current item we will call service from a URL that looks like this:

https://<your site>/_api/web/lists/getbytitle('Locations')/items([%Current Item:ID%])
We will use our call with POST method.



In  Call HTTP service action we need to set the request of the call to the body dictionary we created and the request header to SharepointRequestHeader dictionary


11. This step is optional. I used it to log errors and similar information from the workflow into a field I have added to the list.

You can set the workflow to start when a new item is added so you can still manually update field if you are not satisfied with the coordinates Bing Maps gave you. And thats all, just publish your workflow, insert location information in your Place column (for example : Zagreb,Croatia), wait for the workflow to finish and your geolocation field should fill up automatically.

(I plan to make a short video demo of this workflow so if I didn't explain it too good in this post check back to see if  I was able to make a video available)

Tuesday, May 14, 2013

Using SharePoint REST services from workflow with POST method

SharePoint 2013 has a completely new workflow engine. With that engine we got some new actions and options that help us in creating our bussiness process using workflows. One of them is Call HTTP service action that enables us to leverage an external web service from our workflow.
On the other hand Sharepoint 2013 comes with a new REST interface that enables us to access and use its resources.When we combine these two new features we get powerful new ways for using our SharePoint workflows.

In this post I will show you how you can use this new action to POST new data to your SharePoint site.

I have created a SharePoint list  named WorkflowItems to use for example. We will create items in this list from a workflow using SharePoint REST services. To keep it simple I will leave it with only one column (Title).

Now we can open our SharePoint Designer and create a new site workflow and call it WorkflowItemWizard.

In our new workflow first we will create couple of dictionaries. Dictionary is a new variable type in SharePoint workflows that is used for defining collections of key/pair values and is very important for the Call HTTP service action.

To create a dictionary add Build Dictionary action to you workflow. Using that action specify two new keys Accept and Content-Type and set the values for both of them to application/json; odata=verbose like in the image below


Set the output to a dictionary with a more meaningful name, call it "header". This dictionary will be used as a header for our request.

Next create another dictionary that you will call metadata and in it add a key called type with value SP.Data.WorkflowItemsListItem.

And finally you will need one more dictionary that will contain your request data. Call it parameters, add key __metadata and set its value to the previously created dictionary metadata


Add one more key, call it Title and set its value to New item created from workflow.
Now you are ready to call the web service. Insert action Call HTTP service.
The URL you will require will look like this:
https://<site>/<web>/_api/web/lists/getbytitle('WorkflowItems')/items
and the HTTP method is POST.

Set the request to your parameters dictionary. To set the header of your request go to :
right click on the Call HTTP service -> properties -> RequestHeaders and set it to your header dictionary.


This is how the complete workflow looks like:



Now you can deploy your workflow and run it. Once it is over you should have a new item created in your list.
From this example you can continue building your solutions, you can add more fields, or use other available REST services for updating list items or deleting them.

One of the biggest advantages using this method to create or update list items is it is not limited to the site in which you workflow runs. Using REST services you can now access and create items in lists on other sites.

If you want to know more about SharePoint 2013 REST you should check this out :
Programming using the SharePoint 2013 REST service

Also check this excellent post on getting data into your workflow from SharePoint using Rest services:
Calling the SharePoint 2013 Rest API from a SharePoint Designer Workflow

Sunday, May 5, 2013

Adding geolocation field to SharePoint Online

One of the hidden gems in Sharepoint is the new geolocation field. Geolocation field enables you to annotate SharePoint lists with location information. In columns of type Geolocation, you can enter location information as a pair of latitude and longitude coordinates. The reason it is hidden is because it is not available by default in SharePoint list and there is also no user interface to add it. The only way to add it is through code or powershell. There are already many posts with code samples for adding geolocation field so that wont be the topic of this post. I would suggest to you to check out this post http://zimmergren.net/technical/sp-2013-getting-started-with-the-new-geolocation-field-in-sharepoint-2013 if you want to get a good insight in those code samples. This post will describe how I have created a small wizard form for adding those fields to my SharePoint lists. I am using pure HTML and javascript so this will work both in SharePoint Online and in your on premise environment. First create a page where you wish to place this wizard. On that page add a Script Editor web part. In this web part we will add both our javascript methods and our html.
First add this javascript:


And after that add this HTML form:

Web relative URL: &nbsp
Bing Maps Key: &nbsp
List name: &nbsp
Field name: &nbsp

This will be your end result:


And now you have your form for adding geolocation field anywhere on your site collection. Once you have entered relative path to your web ("/" is root web) you can use Get Bing key to check if you have a BING maps key placed in that web and with Set BING key you can change or add a BING maps key to that web.

And once you enter List name and Field name you can use Create field to add a geolocation field with the name you specified to the list on the web.

Hope this helps you create you geolocation fields faster.

Sunday, April 28, 2013

How to customize you community site badges

In my previous two posts I have shown you how to customize  level achievements of your members on your community site, but there is one more achievement your members can receive - badges.

Badges are special awards that administrators of community sites can give to those members they feel have given extra efforts in the community. Once a member receives a badge it is displayed throughout the site like this:


Badges are nothing more then items in a list that contain just one field (Badge name). Administrators can add new badges or modify existing ones in that list. The list is also called Badges and is created automatically in your community site.

For example my Badges list contains two badges:
Now lets modify their looks. Once again you should find those images that you would like to represent your badges.
For example:



Then upload them somewhere to SharePoint where you can reach them from your community site (I have uploaded them to the Site Assets library of my community site). To make the solution more generic be sure to name your images with the same name of the badge they are representing (like Professional and Expert in my case).

To make those images appear as our badges we will add some javascript and CSS styles. Open your site in SharePoint designer and add this following javascript to your page header




Be sure to match your image extension if it is different from .jpg.
Also we will override two css styles, one for the image (.ms-comm-giftedBadgeIcon) and one for the text of the badge (.ms-comm-giftedBadgeText) to make any necessary adjustments (most likely image size).

This are my styles (I have also added them to my site master):





And here is my end result:



Thursday, April 25, 2013

How to customize your achievement level representation in community site (Part 2)

In the previous part of this post I showed you how you can  customize your achievement level representation
with CSS styles. In this post we will customize it using javascript code  in a way that will allow us to display different images for each level.

First you will need 5 images for each level. For example I will be using these images


each one representing a level of achievements. Upload your images to a place where you can reach them from your community site, I'll upload them to the SiteAssets library of my community site.

Now go to your Community Reputation Settings page
(Site settings -> Communitiy Administration -> Reputation settings)  and under Achievement level representation group select that you wish to diplay your achivement levels as text.

Under the titles for levels we will enter  html tags that will represent our images like this :
  <img src="/mcs/SiteAssets/Rank1.png" alt="" />

Here you can see how my reputation settings look after i have modified them:


If we look how our achievement level is represented now we will see it is still displayed as plain text, not as image.

To display this tags we will have to add some javascript.
Again open your master page in SharePoint Designer and add this script just above </head> tag:
 


And under the script add this following CSS style to override the default style that comes with the text represantation:


 

As you can see I did this to change the height and width of the container that will hold my image so that my image is displayed entirely.

My end result looks like this:


Wednesday, April 24, 2013

How to customize your achievement level representation in community site (Part 1)

In a Community site, as member accumulates points, they reach specific levels as milestones of achievement. Those levels are represented across you site and can be displayed in two ways.

You can display them as :

Images Text

The way levels are displayed can be set through your Community Reputation Settings page
(Site settings -> Communitiy Administration -> Reputation settings) under Achievement level representation group.


We can see that you can choose only what way levels are represented and if they are represented through text, you can specify a title for each level.

But what if you want to change images that represent different levels?

I will show you two different ways to accomplish this.

In my first example I will  replace those little blue squares with an image of  a four leaf clover so when a user achieves another level he will get another clover to represent his new status.

For this I will need two images, one to represent awarded  levels and one to represent remaining levels. I will use this two:



When you create your own  images you will have to upload them somewhere on your SharePoint site where they will be reachable. I've added my images to SiteAssets library of my community site.

If we inspect default images for displaying levels we can see they are nothing more then <div> tags that have some CSS styles that makes them look like blue squares. We will  simply override those styles to represent levels with our own images.

For this we will edit our community site master page in SharePoint designer. To do this open your site in SharePoint Designer and choose Master Pages from the left navigation. If you are not sure which master page you should edit, select each file and the one that has Set as Default button in your designers ribbon greyed out is the one your site is currently using.



When you have opened your master page in designer, you just have to insert this style script above your </head> tag:




Be sure to modify the script so that you point to your images and if needed adjust your height and width. Also you can save this styles to a file and just reference that file in your master page (that would be the proper way to do this).

This is my end result:

I will show you the second way to customize your achievement level representation in part 2 of this post.