Using Nivo slider, knockout.js and SharePoint CSOM to create an announcements Slider

I wanted to create a slider that contains images and items from an announcements in my SharePoint 2010 home page. I started writing my own slider, but realized I was just re-creating the wheel. I ended up finding Nivo Slider. It seemed well supported and clean, so I went with that as my jQuery slider. Let’s dive into the code:

First, here is the HTML code I put on the home page. I just added a CEWP and linked it to a text file in a document library. This is the contents of the text file:

<div class="slider-wrapper theme-default">
    <div id="slider" class="nivoSlider">
        <img src="/PhotoLibrary/cityscape1.jpg" title="#htmlcaption" />
        <img src="/PhotoLibrary/cityscape2.jpg" title="#htmlcaption" />
        <img src="/PhotoLibrary/cityscape3.jpg" title="#htmlcaption" />
        <img src="/PhotoLibrary/cityscape4.jpg" title="#htmlcaption" />
    </div>
    <div id="htmlcaption" class="nivo-html-caption">
	<span data-bind="html: sliderCurrentTitle" style="font-weight:bold;text-decoration:underline;"></span>
	<span data-bind="html: sliderCurrentSummary" ></span>
    </div>
</div>

This interesting parts are lines 9 & 10. I set these two spans to Knockout observable arrays. When the slide changes, I am going to update the contents of these values

And here is the JavaScript document.ready code. Explanation of the code is below the block.

$(document).ready(function () {
    $('#slider').nivoSlider({
        effect: 'sliceDownRight',
        pauseTime: 4000,
        beforeChange: function () {
            VM.sliderNextItem()
        }
    });
ko.applyBindings(VM);
VM.sliderRetrieveAnnouncments();
var VM = new viewModel();
})

Lines 2-8: are the settings for the Nivo Slider.
Line 6: This is the most important part of this part of the code. This calls the function for get the next announcement when the slide changes.
Line 10: This line calls the function that initially loads all the announcements into a JavaScript object.

Here is the code to my Knockout View Model (explanations below):

function viewModel() {
    var self = this;
    self.sliderAnnouncement = function (title, summary) {
        this.title = title;
        this.summary = summary;
    }
    self.sliderAllAnnouncments = ko.observableArray();
    self.sliderCurrentTitle = ko.observable();
    self.sliderCurrentSummary = ko.observable()
    self.sliderCurrentItemIndex = 0
    self.sliderNextItem = function () {
        self.sliderCurrentTitle(self.sliderAllAnnouncments()[self.sliderCurrentItemIndex].title)
        self.sliderCurrentSummary(self.sliderAllAnnouncments()[self.sliderCurrentItemIndex].summary)
        self.sliderCurrentItemIndex++
        if (self.sliderCurrentItemIndex > self.sliderAllAnnouncments().length - 1) {
            self.sliderCurrentItemIndex = 0
        }
    }
    self.sliderRetrieveAnnouncments = function () {
        var clientContext = SP.ClientContext.get_current();
        var oList = clientContext.get_web().get_lists().getByTitle('Announcements');
        var camlQuery = new SP.CamlQuery();
        camlQuery.set_viewXml("<View><Query><Where><And><IsNotNull><FieldRef Name='Title' /></IsNotNull><Gt><FieldRef Name='Expires' /><Value Type='DateTime'><Today/></Value></Gt></And></Where><OrderBy><FieldRef Name='Created' Ascending='False' /></OrderBy></Query></View>");
        var collListItem = oList.getItems(camlQuery);
        clientContext.load(collListItem);
        clientContext.executeQueryAsync(
	    	Function.createDelegate(this, function () {
	    	    var listItemInfo = '';
	    	    var listItemEnumerator = collListItem.getEnumerator();
	    	    while (listItemEnumerator.moveNext()) {
	    	        var oListItem = listItemEnumerator.get_current();
	    	        listItemInfo +=
					'\n' + oListItem.get_item('Title') +
			    	'\n' + oListItem.get_item('Summary');
	    	        self.sliderAllAnnouncments.push(new self.sliderAnnouncement(oListItem.get_item('Title'), oListItem.get_item('Summary')));
	    	    }
	    	    self.sliderNextItem()
	    	}),
	    	Function.createDelegate(this, function () { alert('There was an error - sliderRetrieveAnnouncments') }));
    }
}

Lines 3-6 this is the announcement object that I will use to hold the info of a single announcement
Lines 7-9: this is the observable items that are displayed on the slider that contain the announcement’s title and summary
Line 10: this is the index of the current announcement that is being displayed. All I am doing is incrementing it with the NextItem function
Lines 11-18: This is fired when the slide changes (a builtin hook of the Nivo slider). Once it has changed the item, it increments for the next slide.
Lines 19-40: This is the CSOM query to retrieve all the items from the announcement list that have not expired.
Line 23: This is CAML query to get the items that have not expired.
Line 26: This line makes the query.
Lines 30-36: This code loops through the successful returned items.
Line 35: This line puts each of the items into observable array that is used to house all the announcements.

I hope this is helpful to some one. I have been pretty happy with the outcome.

Update: I saw this slider in action at SharePoint conference: http://corporatenewsapp.codeplex.com/
Looked pretty sweet. May have to try to backport to SharePoint 2010.

Problems with SharePoint 2010 menus and javascript using a Cisco WebVPN (ASA)

We noticed that the SharePoint 2010 menus were not working with our Cisco ASA’s WebVPN. If the top level menu had children, they would not show on hover. Then we started noticing that all jQuery based functions stopped working. It seemed that much of the Javascript used with SharePoint 2010 would not work with our ASA. The fix was to add this to the web.config for the SharePoint site:

 <system.web.extensions>
     <scripting>
           <scriptResourceHandler enableCompression="false" enableCaching="true" />
     </scripting>
</system.web.extensions>

Obviously you are adding the scriptResourceHandler to an exiting scripting section and not replacing what is already there.

Looping through a SharePoint List Column with jQuery and replacing a GUID with a name from CRM 2011

This is a very specialized piece of code, but it came together nicely, so I thought I would share it.

I have a SharePoint List that has a bunch of Microsoft CRM 2011 Contact GUIDs in it. Some columns have one GUID and others have multiple GUIDs separated by semicolons. My goal was: when a user visits the default view for this list, the GUIDs are looked up agains CRM and displayed as the contact’s full name. I wrote the following code to do just that. The 4th, 5th and 10th columns has guide in them. I use this method to loop through the column.

$('table.ms-listviewtable td:nth-child(4),table.ms-listviewtable td:nth-child(5),table.ms-listviewtable td:nth-child(10)').each(function () {
        var guids = $(this).text().split(";");
        var names = ''
        $.each(guids, function () {
            var guid = this;
            var serverUrl = "http://crmserver.company.com"
            var ODATA_ENDPOINT = "/Organization/XRMServices/2011/OrganizationData.svc";
            var ODATA_EntityCollection = "/ContactSet(guid'" + guid + "')";
            var URL = serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection
            $.ajax({
                type: "GET",
                contentType: "application/json",
                datatype: "json",
                async: false,
                url: URL,
                beforeSend: function (XMLHttpRequest) {
                    XMLHttpRequest.setRequestHeader("Accept", "application/json");
                    XMLHttpRequest.setRequestHeader("Content-Type", "application/json")
                },
                success: function (data, textStatus, XmlHttpRequest) {
                    names = names + data.d.FullName + ","
                }
            });
        });
        $(this).text(names.substring(0, names.length - 1));
    });

Line 1: is the jQuery selector for all the columns that have GUIDs. And we loop through each of them.
Line 2: splits the contents of the column.
Line 4: loops through the all the GUIDs in each row/column
Lines 5-9: are setting up the CRM 2011 REST url for the ajax call
Lines 10-24: query the Microsoft CRM 2011 rest endpoint with the contact’s GUID and assign the full name to a variable
Line 25: displays the full name instead of the GUID.

Some slim code that works pretty well.

Using jQuery to hide some columns, append a new column, and popup the contents of the hidden columns

In this previous post, I showed how to hide the 4th and 5th columns of a SharePoint List (I am not talking about EditForm.aspx page, I am talking about the table view). I took this a step further and appended a new row containing an “more info” onClick event that pops up the contents of the hidden columns.

$('td:nth-child(5),th:nth-child(5),td:nth-child(6),th:nth-child(6)').hide()
$('td:nth-child(4)').each(function(){
	$(this).after('<td><img id="MoreInfo" src="/_layouts/images/CNSINF16.GIF" /></td>');
});
$('td.ms-addnew').hide();
$('tr.ms-itmhover').removeClass('ms-itmhover');
$('a[href^="mailto:"]').contents().unwrap();
$('img#MoreInfo').click(function() {
  var notes = $(this).closest('td').next('td').text();
  var description = $(this).closest('td').next('td').next('td').text();
  $('<div><u>Notes</u>: ' + notes+ '<br/><u>Description</u>: '+description +'</div>').dialog()
  });
});

Line 1: hides the 4th and 5th columns
Lines 2 & 3: append a new columns with a clickable gif
Lines 5 & 6: hide the add new and the hoverovers for the rows
Line 7: removes the mailto(s)
Lines 8 – 11: is the onClick for the gif in line 3. This function takes the values of the next two rows and put them in a jQuery modal dialog.

That was fun.

Using _spPageContextInfo, AJAX and SAMAccountName to show a hidden item in a SharePoint page.

I wanted to have a link on a page (could be anything) show only for certain people. I knew there was variable on every page named _spPageContextInfo.userId, which is the current user’s SharePoint user Id. I used the following code to take that userId and query the User Information List to get the SAMAccountName, and then use jQuery to show a hidden link if the use name matches.

    var userId = _spPageContextInfo.userId;
    var SAMAccountName = ''
    var serverUrl = "/_vti_bin/listdata.svc/UserInformationList(" + userId + ")"

    var URL = serverUrl
    $.ajax({
        type: "GET",
        contentType: "application/json",
        datatype: "json",
        async: false,
        url: URL,
        beforeSend: function (XMLHttpRequest) {
            XMLHttpRequest.setRequestHeader("Accept", "application/json");
            XMLHttpRequest.setRequestHeader("Content-Type", "application/json")
        },
        success: function (data, textStatus, XmlHttpRequest) {
            SAMAccountName = data.d.UserName
        }
    });
    if (SAMAccountName.toLowerCase() === 'UserName1'
        || SAMAccountName.toLowerCase() === 'UserName2'
        || SAMAccountName.toLowerCase() === 'UserName3'
        || SAMAccountName.toLowerCase() === 'UserName4'
        ) {
        $('#HiddenId').show()
    }

Looping through a SharePoint List column and acting on each item in the column using jQuery

I wanted to loop through a couple columns in the Default View of a SharePoint List. The jQuery code below would loop through the 4th, 5th, and 10th columns and alert the contents of that column. Obviously you can do anything you like with the matches. More tomorrow on how I was using this method

$('table.ms-listviewtable td:nth-child(4),table.ms-listviewtable td:nth-child(5),table.ms-listviewtable td:nth-child(10)').each(function () {

alert($(this).Text())
}

PowerShell: foreach with a first and last item number

I wanted to work through 1000+ items in a PowerShell foreach loop, but I wanted to do it X at a time. I figured out the following foreach syntax to loop through all items after first and before last:

$First = 15
$Last = 45
foreach ($Row in $Rows | select -first $Last | select -last (($Last - $First)+1)){
. . . .
}

Knockout alternate formattting

I was using KnockoutJS to loop through and display some data. I wanted to apply some alternate formatting on every other row. I used this syntax containing the MOD operation to achieve the formatting I wanted.

<!-- ko if: $index() % 2 === 0 -->

The other thing I learned was that $index() is observable, so I can +1 it? I believe that is right.

The following were some sources that clued me in:

http://jsfiddle.net/KuJBv/11/
https://groups.google.com/forum/#!msg/knockoutjs/ElVix0ksXh8/awkTFYewitAJ

Using PowerShell to add a Contact to a CRM 2011 MarketingList (SOAP)

We had a user delete a Marketing List. I needed to recreate it. I went to a database backup and found the GUID of the deleted list.
Then I used the following SQL query to find the GUIDs of all the members of that list:

SELECT FullName
    ,ParentCustomerIdName
    ,[EntityId]
    ,[ListId]
    ,[ListMemberId]
  FROM [CRMDataBaseName].[dbo].[ListMember],[CRMDataBaseName].[dbo].Contact
  where ListId = '787b77ca-c47d-431b-863e-12a98969b097' AND 
  [EntityId] = ContactId
  order by LastName,FirstName

I saved the EntityId column to a text file, and then I used the following PowerShell code to loop through the GUIDs and add them to a new MarketingList


$ListMembers = Get-Content C:\IT\Temp\ListMemberGUIDs.txt
foreach ($EntityId in $ListMembers){
$xml = ""
$xml += "<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'>";
$xml += "  <s:Body>";
$xml += "    <Execute xmlns='http://schemas.microsoft.com/xrm/2011/Contracts/Services' xmlns:i='http://www.w3.org/2001/XMLSchema-instance'>";
$xml += "      <request i:type='b:AddMemberListRequest' xmlns:a='http://schemas.microsoft.com/xrm/2011/Contracts' xmlns:b='http://schemas.microsoft.com/crm/2011/Contracts'>";
$xml += "        <a:Parameters xmlns:c='http://schemas.datacontract.org/2004/07/System.Collections.Generic'>";
$xml += "          <a:KeyValuePairOfstringanyType>";
$xml += "            <c:key>ListId</c:key>";
$xml += "            <c:value i:type='d:guid' xmlns:d='http://schemas.microsoft.com/2003/10/Serialization/'>5deb4efb-4ed7-47f3-8e8e-bb487e0db423</c:value>";
$xml += "          </a:KeyValuePairOfstringanyType>";
$xml += "          <a:KeyValuePairOfstringanyType>";
$xml += "            <c:key>EntityId</c:key>";
$xml += "            <c:value i:type='d:guid' xmlns:d='http://schemas.microsoft.com/2003/10/Serialization/'>$($EntityId)</c:value>";
$xml += "          </a:KeyValuePairOfstringanyType>";
$xml += "        </a:Parameters>";
$xml += "        <a:RequestId i:nil='true' />";
$xml += "        <a:RequestName>AddMemberList</a:RequestName>";
$xml += "      </request>";
$xml += "    </Execute>";
$xml += "  </s:Body>";
$xml += "</s:Envelope>";
 
$url="http://crm.sardverb.com/SardVerbinnen/XRMServices/2011/Organization.svc/web"
 
$http_request = New-Object -ComObject Msxml2.XMLHTTP
$http_request.Open('POST', $url, $false)
$http_request.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute")
$http_request.setRequestHeader("Content-Type", "text/xml; charset=utf-8")
$http_request.setRequestHeader("Content-Length", $xml.length)
$http_request.send($xml)
}


Powered by WordPress. Designed by WooThemes