• jbmurphy.com stats from last year

    I missed my blogging goal of 2-per-week/104 for the year. I ended up 98 posts. Same goal for this year.
    I also missed 10,000 in a month. I was on track at the beginning of December, but then I had a big drop during the holidays.

    My monthly break down:

    December	 9085
    November	 9827
    October		 9427
    September	 9010
    August		 9440
    July		 9151
    June		 8171
    May 		 7352
    April		 5027
    March		 3966
    February	 3160
    January		 2741
    

    Here is my year end traffic graph from Google Analytics:

    2012-GoogleAnalytics-overview

    And here is the breakdown for the traffic:

    2012-GoogleAnalytics-breakdown

    My 2013 goals are 2 posts per week/104 per year, as I said above. And 15,000 visitors a month. 15k may be out of my reach, but I would love it!


  • 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)
    
    

  • Event calendar I used in my SharePoint 2010 intranet

    This is more of a note for myself, but I thought I would post it anyway, since it my be useful to someone. I used the following event calendar on the front page of my intranet. Small, clean, worked well:

    Event-Calendar-Listing-Web-Part-SharePoint-2010


  • PowerShell 3 – Invoke-WebRequest and Invoke-RestMethod, unable to set the Accept header?

    With both of these commands, when I try to add an Accept header (I want to receive my CRM 2011 data in JSON format, so I need Accept=application/json) I receive the error:

    “This header must be modified using the appropriate property or method.”

    I think this is a bug. This link shows the bug, and I agree that the workaround does not apply


  • PowerShell 3: Invoke-WebRequest vs Invoke-RestMethod and a SharePoint 2010 list with more than 1000 entries

    When using Invoke-RestMethod with a SharePoint 2010 list and ListData.svc, it returns an “System.Xml.XmlElement#http://www.w3.org/2005/Atom#entry” object. Not sure why, but the end result is that you can’t get access to the “rel=next” url, or am I doing something wrong?

    $Results=Invoke-RestMethod -uri $ListUrl -UseDefaultCredentials
    $Results | gm 
    TypeName: System.Xml.XmlElement#http://www.w3.org/2005/Atom#entry
    

    I had to use Invoke-WebRequest and then take the Content and put it in an XML variable, only then could I get to the next page of items.

    $MailingLabels = Invoke-WebRequest -Uri $ListUrl -UseDefaultCredentials
    $Next =  ($MailingLabelsXML.feed.link | ?{$_.rel -eq "next"}).href
    

    Thoughts?


  • SharePoint 2010 modal dialog (showModalDialog) without an existing page

    I was retrieving Activity data from a Microsoft CRM 2011 REST query. I wanted to have a popup with more information. I decided to use the built in showModalDialog. The problem was that all the examples I found showed how to popup an existing page. I wanted the modal to contain data that didn’t exist anywhere. The solution was to use the following code, specifically create a divElement and set the innerHTML to html that contained the data I want to show. I threw in some typical SharePoint css classes to keep the same look and feel as the rest of the site.

        displayActivityModalDialog = function () {
            var divElem = document.createElement('div');
            var htmlOutput = '<div class="ms-bodyareacell"><table>'
            htmlOutput += '<tr><td width="190" class="ms-formlabel">Type:</td><td class="ms-formbody">' + this.ActivityType + '</td></tr>';
            htmlOutput += '<tr><td width="190" class="ms-formlabel">Subject:</td><td class="ms-formbody">' + this.Subject + '</td></tr>';
            htmlOutput += '<tr><td width="190" class="ms-formlabel">Date:</td><td class="ms-formbody">' + this.ScheduledStart + '</td></tr>';
            htmlOutput += '<tr><td width="190" class="ms-formlabel">Regarding:</td><td class="ms-formbody">' + this.Regarding + '</td></tr>';
            htmlOutput += '<tr><td width="190" class="ms-formlabel">Organizer:</td><td class="ms-formbody">' + this.Organizer + '</td></tr>';
            htmlOutput += '<tr><td width="190" class="ms-formlabel">Description:</td><td class="ms-formbody">' + this.Description + '</td></tr>';
            htmlOutput += '<tr><td width="190" class="ms-formlabel">Other Attendees:</td><td class="ms-formbody">' + this.RequiredAttendee + '</td></tr>';
            htmlOutput += '</table></div>'
    
            divElem.innerHTML = htmlOutput;
            var options = SP.UI.$create_DialogOptions();
            options.html = divElem;
            options.title = "Activity details";
            options.showClose = true;
            options.showMaximized = false;
            SP.UI.ModalDialog.showModalDialog(options);
        }
    

    Now I can put anything in a showModalDialog!


  • 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?