SharePoint and Google Maps – How to use them together

Here is an example how to use Google Maps and how to integrate it with SharePoint.

To start with you need to include the following files somewhere in your project

<SharePoint:ScriptLink ID=”JQueryJSFunctionality” runat=”server” Name=”/_layouts/myfolder/scripts/jquery-1.11.2.min.js”></SharePoint:ScriptLink>
<script src=”http://maps.google.com/maps/api/js?sensor=false&#8221; type=”text/javascript”>
</script>

<script type=”text/javascript”>
var listName = ‘<%=ListName %>’; // This can be used to pass a list name from web part properties to the JavaScript/JQuery logic, this is a propert defined in the code behind of a user control
</script>

<!–<SharePoint:ScriptLink Name=”SP.js” runat=”server” OnDemand=”true” Localizable=”false” />–>
<SharePoint:ScriptLink ID=”ScriptLink1″ Name=”sp.debug.js” LoadAfterUI=”true” Localizable=”false” runat=”server” />
<SharePoint:ScriptLink ID=”userWorldMapJSFunctionality” runat=”server” Name=”/_layouts/myfolder/scripts/yourGoogleMapsLogicFileName.js”></SharePoint:ScriptLink>

 

Next is the JavaScript/JQuery code

Create instances of needed Google Maps functionalities

var gmGeocoder = new google.maps.Geocoder();
var gmBounds = new google.maps.LatLngBounds();

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

var gmMap = new google.maps.Map($(“#map_canvas”)[0], mapOptions);

// You can use this to listen when the Google Maps has finnished its operations
google.maps.event.addListenerOnce(gmMap, ‘idle’, function () {

});

 

// You can use this function class to pass on information to the Google Maps geo location finder

var street = “”;
var zip = “”;
var city = users[iter].city;
var country = users[iter].country;
address = street + “,” + zip + “,” + city + “,” + country;

gmGeocoder.geocode({ ‘address’: address }, function (user, allUsers) {
return (function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results.length == 0) {
//console.log(“Geocoding: There are no results for address ‘” + results[0].formatted_address + “‘! Expected exactly one result. > Do not show any marker on map for this address.”);
}
else {

// Here is where you get a results back for the specified address and start to construct data for a Google Maps marker
var externalCount = 0;
var internalCount = 0;
gmLatLng = results[0].geometry.location;
//markerBounds.extend(gmLatLng);
var msgForInfoWindow = “<div id=’slider1′ >”;
var colorValue = “red”;
if (user.isexternal == true) {
colorValue = “blue”;
externalCount++;
} else {
internalCount++;
}
var orderID = 0;

// The actual content that goes into the marker starts here
msgForInfoWindow += “<div class=’viewport’>”;
msgForInfoWindow += “<ul class=’overview’>”;

 

orderID++;
msgForInfoWindow += “<li>”;
msgForInfoWindow += “Add your data here”;
msgForInfoWindow += “</li>”;
msgForInfoWindow += “</ul>”;
msgForInfoWindow += “</div>”;
msgForInfoWindow += “<a class=’buttons prev’ href=’#’><</a>”;
msgForInfoWindow += “<a class=’buttons next’ href=’#’>></a>”;

msgForInfoWindow += “</div>”;
var isExternalMarker = false;
if (externalCount > internalCount)
isExternalMarker = true;

// Calling this function will create a maker based on the given parameters
SetMarkerForGoogleMapForSharePointList(gmLatLng, gmMap, gmBounds, user.city + “, ” + user.country, msgForInfoWindow, isExternalMarker);
}
}
else {
//console.log(“Geocode for address ‘” + address + “‘ was not successful for the following reason: ” + status);
}
});
} (users[iter], users));

//getting coordinates
gmGeocoder.geocode({ ‘address’: address }, function (results, status) {

});

 

function NullVerification(stringValue) {
var returnValue = ” “;
if (stringValue != null)
returnValue = stringValue;

return (returnValue);
}

function NullVerificationBool(stringValue) {
var returnValue = false;
if (stringValue != null)
returnValue = true;

return (returnValue);
}

 

function SetMarkerForGoogleMapForSharePointList(gmLatLng, gmMap, gmBounds, title, contentForInfoWindow, isExternalMarker) {

// This is just a logic on how to control the marker styling, you can use your own

var icon = “”;

if (isExternalMarker)
icon = “yellow”;
else
icon = “red”;
icon = “http://maps.google.com/mapfiles/ms/icons/&#8221; + icon + “.png”;

// Create the marker for the Goole Map
var gmMarker = new google.maps.Marker({
position: gmLatLng,
map: gmMap,
title: title,
zIndex: 0,
icon: new google.maps.MarkerImage(icon)
});

// This will center the map based on given marker positions
gmBounds.extend(gmLatLng);
gmMap.setCenter(gmBounds.getCenter());
gmMap.fitBounds(gmBounds);

// Define properties for the info window that is opened when the marker is pressed

if (contentForInfoWindow != null && contentForInfoWindow != “”) {
var gmInfowindow = new google.maps.InfoWindow({
content: contentForInfoWindow,
maxWidth: 500,
maxHeight: 600
});

// This will attach a listener to the marker to that we can apply logic when it is opened.

google.maps.event.addListener(gmMarker, ‘click’, function () {
gmInfowindow.open(gmMap, gmMarker);
$(‘#slider1’).tinycarousel({ bullets: true });
var t = 0;
});
}
}

// This is an example how to retrieve data from a SharePoint list using JavaScript 

function retrieveListItems() {

var clientContext = SP.ClientContext.get_current();
if (IsEmpty(listName))
listName = ‘UsersMapData’;
var oList = clientContext.get_web().get_lists().getByTitle(listName);

var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(‘<View><Query><Where><And><IsNotNull><FieldRef Name=”Country” /></IsNotNull><And><IsNotNull><FieldRef Name=”City” /></IsNotNull><IsNotNull><FieldRef Name=”PersonName” /></IsNotNull></And></And></Where><OrderBy><FieldRef Name=”ID” Ascending=”True” /></OrderBy></Query></View>’);
this.collListItem = oList.getItems(camlQuery);

clientContext.load(collListItem);

clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));

}

 // When the data retrieval from the list succeeds this function is called. Here the needed data is then retrieved and parsed. Then passed on to be used later

function onQuerySucceeded(sender, args) {

var listItemInfo = [];

var listItemEnumerator = collListItem.getEnumerator();

// Here the data from SharePoint is went through and items are retrieved to container objects to be used later

while (listItemEnumerator.moveNext()) {

var oListItem = listItemEnumerator.get_current();
var newUserData = { id: oListItem.get_id(),
name: oListItem.get_item(‘PersonName’),
country: oListItem.get_item(‘Country’),
city: oListItem.get_item(‘City’),
imageLocation: oListItem.get_item(‘UserImageLocation’)
};
listItemInfo.push(newUserData);
}

return (listItemInfo);
}

function onQueryFailed(sender, args) {

alert(‘Request failed. ‘ + args.get_message() + ‘\n’ + args.get_stackTrace());
}

SharePoint 2013 Blog – Change the author of the post with a custom selected user

This is a sample JS to use with the posts webpart in the SharePoint blog(JSLink webpart property). Originally I wanted to the blog.xsl but I simply could not get it to world desirably in SharePoint 2013. Well it worked in a different manner that what it used to be in SharePoint 2010.

Here is the code with comments:

var userID = 0;
var globalListItemIDs = [];
var spjsLoaded = false;
var listName = “Viestit”;
var customUserFieldInternalName = “PostOwner”;

// Main function – Start point
(function () {

var overrideCtx = {};

overrideCtx.Templates = {};

// Register a template override for the author field
overrideCtx.Templates.Fields = {‘Author’:{‘View’:CBody}};

SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
})();

function CBody(ctx) {

// Load SP client context, ote async event
SP.SOD.executeFunc(‘sp.js’, ‘SP.ClientContext’, sharePointReady);

// create a point of focus where to add the loaded information from the post item user field, Notice taht we are using the list item ID as an identifier where to put our user data
var ret = “<b><span class=’Blog_userTemplateContextData’ id=’userTemplateContextData” + ctx.CurrentItem.ID +”‘></span></b>”
var newListItemData = { id: ctx.CurrentItem.ID };

globalListItemIDs.push(newListItemData);

//var ret = ctx.CurrentItem.Body;

return ret;

}

function sharePointReady() {
if(spjsLoaded == true)
return;
spjsLoaded = true;

clientContext = SP.ClientContext.get_current();
website = clientContext.get_web();

var oList = clientContext.get_web().get_lists().getByTitle(listName);

// Check if a specific post list item is specified, this would mean that the user is viewing a sigle post
var listItemID = getParameterByName(‘ID’);

// If no single post id is found then load all available posts and their custom users to be used instead of the author(TODO: Optimize for displayed posts only)
if(listItemID === null || listItemID === “”)
{
/*for (var iter = 0; iter < globalListItemIDs.length; iter++)
{
listItemID = globalListItemIDs[iter].id;
MakeNewDataCall(oList, clientContext, listItemID);
}*/
var camlQuery = new SP.CamlQuery();

camlQuery.set_viewXml(‘<Query><OrderBy><FieldRef Name=”Created” Ascending=”False” /></OrderBy></Query>’);
this.collListItem = oList.getItems(camlQuery);

clientContext.load(collListItem);

clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
} else
{
// Only a single post is needed
MakeNewDataCall(oList, clientContext, listItemID);
var newListItemData = { id: listItemID };

globalListItemIDs.push(newListItemData);
}

}

function MakeNewDataCall(oList, clientContext, listItemID)
{
var camlQuery = new SP.CamlQuery();

camlQuery.set_viewXml(‘<Query><Where><Eq><FieldRef Name=”ID” /><Value Type=”Counter”>’ + listItemID + ‘</Value></Eq></Where><OrderBy><FieldRef Name=”Created” Ascending=”False” /></OrderBy></Query>’);
this.collListItem = oList.getItems(camlQuery);

clientContext.load(collListItem);

clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}

function onQuerySucceeded(sender, args) {

var listItemInfo = [];

var listItemEnumerator = collListItem.getEnumerator();

// Get the post data(single or many) and process it
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
var listItemID = oListItem.get_id();

// Seek the SharePoint generated posts and seek if the current item is something we want to process
var userDataDiv = document.getElementById(“userTemplateContextData” + listItemID);
if(!(userDataDiv === null))
{
var author = oListItem.get_item(‘Author’);
// IF so then get the custom user information
var personOwner = oListItem.get_item(customUserFieldInternalName);
if(!(personOwner === null))
{
var name = personOwner.get_lookupValue();

// Put default data that is going to be used if a user image is not found, Notice that we define a user image location
userDataDiv.innerHTML = “<span class=’Blog_postUserData’ id=’postUserData” + personOwner.get_lookupId() +”‘>” + name + “</span>”;
var context = SP.ClientContext.get_current();
var web = context.get_web();
var userInfoList = web.get_siteUserInfoList();

// Make a call to the user profile information for this user to see if there is a user image available
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(‘<View><Query><Where><Eq><FieldRef Name=”ID”/><Value Type=”Number”>’ +personOwner.get_lookupId()+ ‘</Value></Eq></Where></Query><RowLimit>1</RowLimit></View>’);
this.listItems = userInfoList.getItems(camlQuery);
context.load(listItems);
context.executeQueryAsync(Function.createDelegate(this, this.onProfileSuccessMethod), Function.createDelegate(this, this.onQueryFailed));
}
}
}
}

function onProfileSuccessMethod(sender, args) {
var item = listItems.itemAt(0);
var firstName = item.get_item(‘FirstName’);
var lastName = item.get_item(‘LastName’);
var userId = item.get_id();
// Get the location where the user image is to be inserted
var userDataDiv = document.getElementById(“postUserData” + userId);
if(!(userDataDiv === null))
{
// If there is an image then use it, if no image is provided do nothing and display the user full name instead.
var picture = item.get_item(‘Picture’);
if (picture!=null) {
userDataDiv.innerHTML = “<img class=’Blog_userDataImgClassStyle’ src='” + picture.get_url() +”‘ height=’42’><span class=’Blog_postUserDataName’ id =’postUserDataName’>” + firstName + ” ” + lastName + “</span>”;
}
}
}

function onQueryFailed(sender, args) {

alert(‘Request failed. ‘ + args.get_message() + ‘\n’ + args.get_stackTrace());
}

function getParameterByName(name) {
name = name.replace(/[\[]/, “\\[“).replace(/[\]]/, “\\]”);
var regex = new RegExp(“[\\?&]” + name + “=([^&#]*)”),
results = regex.exec(location.search);
return results === null ? “” : decodeURIComponent(results[1].replace(/\+/g, ” “));
}

Alter or Style SharePoint List forms (New, Edit and Display)

With the JS code below you can modify a SharePoint form view to your wishes. I commented what is important and highlighted such code and HTML with the green color.

In the example below there is one part that I have to explain a little better:

$(“.ms-formtable tr td.ms-formlabel h3.ms-standardheader a[name=’SPBookmark_your_field_id’]”).parent().parent().parent().children(“td.ms-formbody”)

The “parent.parent.parent.children” part means that we need to get the cell with the actual value. The reason why I did not try to directly to get the value with jquery but went to the value through a work around is because I had problem getting the value. The value is populated by SharePoint javascript meaning that I was not able to find a good way to identify the exact table cell where a particular value of a field resided. If you have a better solution please let me know in the comments, thx :).

JQuery code to find and perform styling and possible data alteration to a Display form table cell(s):

var SPFormsFunctionalities = {
AlterAccountsLedgerDisplayForm: function () {
// Make sure that we only operate on SP Display Forms and nothing else
if (window.location.href.indexOf(“DispForm.aspx?ID=”) > -1) {

// Remove any SharePoint adittional comments in the TD to get to the actual datetime value
$(“.ms-formtable tr td.ms-formlabel h3.ms-standardheader a[name=’SPBookmark_your_field_id’]”).parent().parent().parent().children(“td.ms-formbody”).contents().filter(function () {
return this.nodeType == 8;
}).remove();

// Get the value and remove any whitespaces to make sure that there is actual data or if we want to process that data
var creditStoppageValue = $.trim($(“.ms-formtable tr td.ms-formlabel h3.ms-standardheader a[name=’SPBookmark_your_field_id’]”).parent().parent().parent().children(“td.ms-formbody”).html());

// If the value is not empty then highlight it
if (!SPFormsFunctionalities.empty(creditStoppageValue)) {
$(“.ms-formtable tr td.ms-formlabel h3.ms-standardheader a[name=’SPBookmark_your_field_id’]”).parent().css(“color”, “red”);
$(“.ms-formtable tr td.ms-formlabel h3.ms-standardheader a[name=’SPBookmark_your_field_id’]”).parent().parent().parent().children(“td.ms-formbody”).css(“color”, “red”);
// Make sure that we only operate on SP Edit Forms and nothing else
} /*else if (window.location.href.indexOf(“EditForm.aspx?ID=”) > -1) {
alert(“found EditForm”);

}*/
}
},
empty: function (data) {
if (typeof (data) == ‘number’ || typeof (data) == ‘boolean’) {
return false;
}
if (typeof (data) == ‘undefined’ || data === null) {
return true;
}
if (typeof (data.length) != ‘undefined’) {
return data.length == 0;
}
var count = 0;
for (var i in data) {
if (data.hasOwnProperty(i)) {
count++;
}
}
return count == 0;
}
};

if ($) {
$(document).ready
(
function () {
SPFormsFunctionalities.AlterAccountsLedgerDisplayForm();
}
);
}

The HTML upon which the JQuery code above would operate on

<table class=”ms-formtable” style=”margin-top: 8px;” border=”0″ cellpadding=”0″ cellspacing=”0″ width=”100%”>

<tr>
<td nowrap=”true” valign=”top” width=”113px” class=”ms-formlabel“><h3 class=”ms-standardheader”><a name=”SPBookmark_sp_internal_field_name_goes_here”></a>your field title is here</h3></td>
<td valign=”top” class=”ms-formbody” width=”350px” id=”SPFieldDateTime”>
<!– FieldName=”SP field name is here”
FieldInternalName=”sp internal field name is here”
FieldType=”SPFieldDateTime”
–>
<span id=’WPQ2da070d16-f172-4faf-8060-029b2a734fc6sp_internal_field_name_goes_here’ data-sp-control=’SPFieldDateTime’ data-sp-options='{&quot;mode&quot;:1,&quot;source&quot;:&quot;sp_internal_field_name_goes_here&quot;}’></span>

</td>
</tr>
</table>

Sources:

The empty function is from: http://www.sitepoint.com/testing-for-empty-values/

 

Notice: That this example was tested against SharePoint 2010

Social Media hyperlink(URL) copy&paste metadata

To be specific on what is shown in social media when you copy&paste a link from you web application or SharePoint application you have to define HTML headers meta-tags. Most social medias such as Facebook, Twitter, Google+ and LinkedIn support the Open Graph Protocol. It is rather straight forward on to use it. Check our the web site:

http://ogp.me/

For C# developers you can use the HtmlMeta class to perform your needs: HtmlMeta Class

Or you could use a combination of HTML and embbeded code

 

Programatically forcing print styles to a web page in SharePoint

If you have a situation where you want to force print styling(or any other styling) on a page request made from any machine or application you have two options:

1. You identify the HTTP request header information(user agent) and see what application/source has requested this page request.

2. Specify a query string parameter that when encountered in a page request the wanted styling will be applied.

Basically both options have the same code which I will explain below. They only differ in the way the recognition of the printing request is done. In this example I will show the option number two.

A simple approach is to use a master page code or you could do it with a HttpHandler but a mater page approach is faster to do.

The code is rather simple:

  1. Identify the query string key and get the value
  2. Insert in the PlaceHolderAdditionalPageHead content placeholder control your CSS registration. This will add your CSS registration in the header of the HTML output. Notice that you gotta make sure your content placeholder is preferably just before the ending of the </head> HTML tag. This is to make sure that you CSS will overwrite anything else done before.

Here is the code:

String qsID = Request.QueryString[“myquerystingkey”];

            if (!String.IsNullOrEmpty(qsID))

            {

                switch (qsID.ToLower())

                {

                    case “print”:

                        {

                            if (this.PlaceHolderAdditionalPageHead != null)

                            {

                                this.PlaceHolderAdditionalPageHead.Controls.Add(new Literal() { Text = “<link rel=’stylesheet’ type=’text/css’ media=’all‘ href=’printer.css’>” });

                            }

                        } break;

                }

            }

 

 

Basically you can apply this to any form of web application just replace the page header control with some other method of inserting your CSS file reference. Also take notice that for this to work the media attribute on the link reference requires to be in the ALL mode not print. If it is set to print then you would have to make a print request to the browser. to do this you could do one of the following:

  1. Create a link button to perform the print: <A HREF=”javascript:window.print()”>Print me</A>
  2. Or you can do something similar what we did above with the CSS and insert a javascrip block with the invocation of the print request:

<script type=”text/javascript”>
javascript:window.print();
</script>

SharePoint Language specific CSS loading

Today I worked on a annoying problem where I had to figure out how to do a language specific CSS file loading during header load from a Master Page.

The solution is as following in our case:

Our code deploys under the LAYOUTS folder our project style sheets, js files etc. We created for each language ID it’s own folder and under each language folder the css file that we wanted to use for a each language.

Next we addedthe following line inside the <HEAD> element and the right CSS file based on the language ID was loaded:

<SharePoint:CssRegistration name=”<% $SPUrl:/_layouts/projectname/styles/~language/master.css %>” runat=”server”/>

In the definition above what is in bold and in blue is what you MUST add for the reference to work. What is in green and not in bold is your path where your CSS file is located. Change it to fit your needs.

What is the most interesting part is the “~language” definition in the URL. This will retrieve the proper language ID(example for English-US: en-US). The final URL reference will look something like this:

/_layouts/projectname/styles/en-US/master.css

How to deal with a single long continuous word in CSS and HTML

If you have a very long piece of text that has no white spaces or anything that would make the word into smaller bits or information this will likely cause problem when rendered by the browser. Might cause your content to explode width wise.

In one on my problem cases I was creating a custom XSLT to display email related data. One of the data was the To information of emails which holds the recipients of an email. If the email had lots of recipient they where one after another in a single continuous string. This caused a problem when the data was retrieved from the CMS system and displayed to the user. Width wise everything exploded.

What we did in our case the had a table into which the data was inserted. The TD element was modified so that the content would be scroll able and forced to a certain width. This may help someone if you have a similar problem.

<td style=”WIDTH: OVERFLOW: hidden” class=”ms-vb”>
<span style=”DISPLAY: block;WIDTH: 257px; OVERFLOW: auto”>
<xsl:value-of select=”fieldname”/>
</span>
</td>