Archive | jQuery

A better jQuery selector to hide a row in a SharePoint 2010 Edit Form

In the past, I had used this code to hide a row in a SharePoint Edit Form (EditForm.aspx):

$('nobr:contains("Attendees")').closest('tr').hide();

The problem with this is that if you have two fields that contain the text “Attendees” (For example “NewAttendees” & “OldAttendees”), then it matches both of them (this always seems to to be an issue for us). So I did a little research and I found a better way to select a row (I think this was my original source)

Here is my new/better way to match a row:

 $('nobr').filter(function () { return $(this).text() === 'Attendees' }).closest('tr').hide();

If it was a required field, it would be:

 $('nobr').filter(function () { return $(this).text() === 'Attendees *' }).closest('tr').hide();

Hope that helps someone.

Using jQuery to change to the Items tab in a SharePoint 2010 list.

In my last post, I showed how to use a query string parameter to start a user out on the items tab of a SharePoint list. This is well documented, I just can’t seem to remember it (that is why I posted it!). I wanted to take it a step further and use jQuery to change to the Items tab. Here is the code to do that:

$(document).ready(function () {
    ExecuteOrDelayUntilScriptLoaded(function () {
          _ribbonStartInit("Ribbon.ListItem", false, null);
    }, "sp.ribbon.js");    
});

The trick was to make sure the sp.ribbon.js script was loaded, and then use the included function to change to the preferred tab. Hope that helps someone.

Replace the New Item button on a SharePoint 2010 list with jQuery

I have a SharePoint 2010 Business Data Catalog (BDC) pointing to our MS CRM 20111 back end. This is a good way to show paged views of the database. But at the top, there is still a “New Item” button. This does not make much sense for a BDC (or External list) without update queries. I wanted a workaround. I wanted to “hijack” this button – Make it do what I want. This is the jQuery code to do that!
Basically I am hiding the existing button, and replacing it with what I want.

    $("#RibbonContainer").ready(function () {
        var imageRow = '<span class="ms-cui-ctl-large" onclick=\"window.location=\'/Destination/YouWant/NewForm.aspx?source=/Lists/BDCList/Default.aspx?InitialTabId=Ribbon.ListItem\'; \") > \
            <a class="ms-cui-ctl-a1 "  href="javascript:;" > \
            <span class="ms-cui-ctl-a1Internal" unselectable="on"> \
            <span class=" ms-cui-img-32by32 ms-cui-img-cont-float" unselectable="on"> \
            <img class="" style="left: -64px; top: -320px;" src="/_layouts/1033/images/formatmap32x32.png" /> \
            </span> \
            </span> \
            </a> \
            <a class="ms-cui-ctl-a2"><span class="ms-cui-ctl-largelabel"> New <br/> Item </span></a> \
            </span>'
        $("#s4-ribboncont .ms-cui-ctl-large:contains('New Item')").hide()
        $("#s4-ribboncont .ms-cui-ctl-large:contains('New Item')").before(imageRow)
    }); 

A simple javascript/AJAX function to post a SOAP request to CRM 2011

This is a simple function that I use to post a SOAP envelope to CRM 2011. Just pass the URL and the xml (you can create that with this function) and you should be good to go

    function soapToCRM(URL, data) {
        var returnValue
        $.ajax({
            type: "POST",
            contentType: "text/xml; charset=utf-8",
            datatype: "xml",
            async: false,
            url: URL,
            data: data,
            beforeSend: function (XMLHttpRequest) {
                XMLHttpRequest.setRequestHeader("Accept", "application/xml, text/xml, */*");
                XMLHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
                XMLHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
            },
            success: function (data, textStatus, XmlHttpRequest) {
                //alert("success");
                var NewCRMRecordCreated = data["d"];
                returnValue = true
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                alert("failure " + errorThrown);
                returnValue = false;
            }
        });
        return returnValue;
    }

Using AJAX and SOAP to create a CRM 2011 activity

I have posted a bunch of PowerShell scripts to interact with CRM 2011, now I am going to put up similar javascript versions. I feel that there is a lot of javascript content out there for CRM 2011, but most of it is javascript inside the actual CRM interface. I don’t see a lot of content about using javascript from a different webpage/site with ajax, so I thought I would post some of the code I put together.

This is a function that I put together to build a SOAP envelope for creating an activity in CRM 2011. Credit for the template goes to Jamie Miley and this post. I took his template a little further and created a function for either email,Phone call or appointment. In addition this function allows for multiple contacts in an phone call’s to and multiple required contacts in a meeting.

Continue Reading →

How to hide a field in a SharePoint EditForm.aspx page, append a replica, add a jQuery autocomplete, and put the selected value in the original field.

If you look at this old post, you can see a technique that I used to hide a form field, and then append read-only data after it. I wanted to use this technique to hide a field in a form, append a replica with a jQuery autocomplete, and based on the selected value from the drop down, put the value in the original filed. I actually wanted a comma separated concatenation of all the selected values (multiple lookups). For example, I wanted to create a form to capture all the people at a meeting, the the attendees field would be hidden and replaced with a input box that can lookup contact GUIDs from a CRM, and once the contact is selected, the GUID is appended to the contents of the original Attendees field.

First up, the code to hide the field I want:

	attendeesRow='<tr id="attendeesRow"> \
		<td nowrap="true" valign="top" width="190px" class="ms-formlabel"><h3 class="ms-standardheader"><nobr>Attendee<nobr></h3></td> \
		<td valign="top" class="ms-formbody" width="400px"> \
		<div id="AddAttendees"><input type="text" id="AddAttendeesSearchTextbox" /> (add an EXISTING CRM Contact)</div><br/> \
		</td></tr>';
	$('nobr:contains("Attendees")').closest('tr').hide();
	$('nobr:contains("Attendees")').closest('tr').before(attendeesRow);

Lines 1-5 is the code for the new replica field
Line 6 hides the existing field
Line 7 prepends the new replicate created in Lines 1-5

Next is the jQuery code to attach an autocomplete to the new text box (AddAttendeesSearchTextbox). I am using a little knockout to organize my code and I use some of the observable arrays to make the page more dynamic.

$('#AddAttendeesSearchTextbox).autocomplete({
	    source: function (request, response) { VM.contactsSearchSourceREST(request, response) },
		delay: 600,
		minLength: 3,
		select: function(event, ui) {
			var selectedObj = ui.item;
			VM.addAttendee(selectedObj.fullname,"","",selectedObj.ParentCustomerIdName,"5",selectedObj.id)
			$(this).val("");
			return false;
		}
	});

And here is the javascript code (part of the view model) that is used for the source of the jQuery AutoComplete (CRM 2011 oData REST endpoint). Just a simple ajax call to CRM 2011.

	self.contactsSearchSourceREST = function (request, response) {
	    var serverUrl = "crm.server.com"
	    var ODATA_ENDPOINT = "/ORGNAME/XRMServices/2011/OrganizationData.svc";
	    var ODATA_EntityCollection = "/ContactSet";
	    var strSelect = "$select=FullName,ParentCustomerId,ContactId"
	    var strFilter = "$filter=substringof('" + request.term + "',FullName) and StateCode/Value eq 0"
	    var URL = serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection + "?" + strFilter + "&" + strSelect
	    //alert(URL);
	    $.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) {
	            response($.map(data.d.results, function (item) {
	                return {
	                    label: item.FullName + ' (' + item.ParentCustomerId["Name"] + ')',
	                    value: item.FullName + ' (' + item.ParentCustomerId["Name"] + ')',
	                    fullname: item.FullName,
	                    ParentCustomerIdName: item.ParentCustomerId["Name"],
	                    id: item.ContactId
	                }
	            }));
	        },
	        error: function (XMLHttpRequest, textStatus, errorThrown) {
	            alert("failure " + errorThrown);
	            return false;
	        }
	    });
	}

Next is the knockout code (part of the view model) that is used to push the new contact guid into an observable array upon selecting the AutoCompleted contact

    self.Attendees = ko.observableArray();
    self.contact = function(fullname, firstname, lastname,ParentCustomerIdName, activitypartytype, guid) {
	this.fullname = fullname;
	this.firstname = firstname;
	this.lastname = lastname;
	this.ParentCustomerIdName = ParentCustomerIdName;
	this.role = activitypartytype;
	this.guid = guid;
    };
    self.addAttendee = function(fullname, firstname, lastname,ParentCustomerIdName, activitypartytype, guid) {
        self.Attendees.push(new self.contact(fullname, firstname, lastname,ParentCustomerIdName, activitypartytype, guid));
        return
    };

And finally the knockout code to concatenate the GUIDs and put them in the hidden (original) field (Attendees).

    ko.utils.arrayForEach(self.Attendees(), function(contact) {
        total = total + contact.guid + ';'
    	});
    total = total.substring(0, total.length - 1);
    $('input[type=text][title="Attendees"]').val(total)
    return total
	});

Pretty complex, lots of different techniques bing used (knockout, jQuery, ajax). Hope it makes sense.

Using jQuery to add a new item to the breadcrumbs at the top of a SharePoint 2010 site.

I wanted to add a bread crumb item to a sub site that pointed back to the home page. This is the jQuery code I used to insert it. This code also aadds the little arrow divider


var NavItem ='<a id="ctl00_PlaceHolderSiteName_onetidProjectPropertyTitle" href="/">Home</a>'

NavItem = NavItem + '<span id="onetidPageTitleSeparator" class="s4-nothome s4-bcsep s4-titlesep"><span><span style="height:11px;width:11px;position:relative;display:inline-block;overflow:hidden;"><img src="/_layouts/images/fgimg.png" alt=":" style="border-width:0px;position:absolute;left:-0px !important;top:-585px !important;" /></span></span> </span>'

$(".s4-titletext h1").prepend(NavItem)

SharePoint 2010, CSOM and External Lists – must use LoadQuery

I was working on some JavaScript code to pull data from an External List that was pointing to a Microsoft CRM 2011 database. The code was being used to populate a jQuery autocomplete like this post. In that post you can see on line 10, I use the “load” method. This worked, but if I started a new query before the previous one ended, a javascript error was thrown. I am sorry I don’t recall the error – I am blogging this a month or so after I figured it out.

It took me a while, but I found one reference to figure out the issue. It is on page 143 of Scott Hillier’s book “Professional Business Connectivity Services in SharePoint 2010” (I found it via the google query “loadquery external list”)

The work around was that you have to use LoadQuery with External lists, Load is not supported. Not sure where this is documented, but it took me quite a while to figure that one out.

Hope that helps some one?

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.