Archive | SharePoint

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)
    }); 

My upgrade of SharePoint 2007 to 2010 “script”

One of my most recent projects was the migration of our intranet from SharePoint 2007 to 2010. Since we were going to change the name of the site, I was able to run through this “script” several times as practice to make sure I had everything correct.

I decided to do a detach and attach method. Here are some of the things we did.

  1. We preformed several test a detach and attach upgrades with the new URL. This allowed us to test everything using the new url, and make changes back in the original 2007 site so that it would work once we performed the final live cutover.
  2. All new code/pages/hacks were added to the 2010 site into New documents libraries. These were backed up using this script and restored after every new test detach an attach test. This way all new code would be in place with the final live cutover.
  3. Since we were doing a new Navigation, we created the navigation in the old 2007 site, and hid them by audience. Then one of the steps below is to change the audience which would un-hide the new navigation in the final cutover.

Step 1. Backup all the new code/pages/hacks that have been added to the new site that needs to be restored.

$folderDate=$(get-date -uformat &quot;%Y-%m-%d&quot;)
$folderHour=$(get-date -uformat &quot;%H&quot;)
$backupDir=&quot;\\Path\To\Backup\$folderDate\$folderHour&quot;

foreach ($web in $(Get-SPSite | Get-SPWeb)){
    foreach ($list in $web.Lists) {
    mkdir -force &quot;$backupDir\$($Web.Title.replace(' ','').replace('/','-'))\&quot;
    Export-SPWeb $($web.Url) -itemurl &quot;$($list.RootFolder.ServerRelativeUrl)&quot; -path &quot;$backupDir\$($Web.Title.replace(' ','').replace('/','-'))\$($list.Title.replace(' ','').replace('/','-')).cmp&quot;
    }
}

Now we have captured all the changes that were made to the new site (which we will be restoring after the next cutover test)

Step 2. Remove the previous test cutover site

Remove-SPWebApplication &quot;SharePoint - intranet.company.com80&quot; -RemoveContentDatabases -DeleteIISSite

Step 3. Re-create new app and apppool.

New-SPWebApplication -Name &quot;SharePoint - intranet.company.com80&quot; -Port 80 -HostHeader intranet.company.com -URL &quot;http://intranet.company.com&quot; -ApplicationPool &quot;SharePoint - intranet.company.com80&quot;

Step 4. remove the content database that is created by default

Get-SPContentDatabase -WebApplication &quot;SharePoint - intranet.company.com80&quot; | Remove-SPContentDatabase

Step 5. Backup 2007 site to a share and restore to new SQL server (or new db name on existing SQL server).

Backup:

$SRCSQLSERVER='OldSQLServer'
$DESTSQLSERVER='NewSQLServer'
$sqlcmdBackup=&quot;BACKUP DATABASE [Content_Intranet] TO DISK = N'\\path\to\network\share\Content_Intranet.bak' WITH NOFORMAT, NOINIT,  NAME = N'Content_Intranet FullBackup', SKIP, NOREWIND, NOUNLOAD,  STATS = 10&quot;
invoke-sqlcmd -query &quot;$sqlcmdBackup&quot; -Server $SRCSQLSERVER -QueryTimeout 1200

Restore:

$sqlcmdRestore=&quot;RESTORE DATABASE [Content_Intranet] FROM  DISK = N'\\path\to\network\share\Content_Intranet.bak' WITH  FILE = 1,  MOVE N'Content_Intranet' TO N'K:\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\Content_Intranet.mdf',  MOVE N'Content_Intranet_log' TO N'K:\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\Content_Intranet_log.LDF',  NOUNLOAD,  REPLACE,  STATS = 10&quot;
invoke-sqlcmd -query &quot;$sqlcmdRestore&quot; -Server $DESTSQLSERVER -QueryTimeout 1200

Step 6. Mount the restored database and upgrade the uner experience.

Mount-SPContentDatabase -Name Content_Intranet  -DatabaseServer NewSQLServer -WebApplication http://intranet.company.com -Updateuserexperience

Step 7. Re-import exported Document Libraries that contain the new code/pages/apps

Import-SPWeb http://intranet.company.com -Path \\path\to\network\share\date\hour\LibraryName.cmp

Step 8. Clean up navigation by changing audience, change homepage to new page in restored Document Libraries.

Step 9. Alter web.config for Cisco WebVPN

Step 9. Allow inline PDFs

That was it. I did it several times, and it ended up being a smooth cutover.

PowerShell command to allow inline PDF viewing in SharePoint 2010

My users like to view PDFs in their browser on our SharePoint site. I needed to allow this in 2010:
Here is the powershell to allow inline PDF viewing in SharePoint 2010

$webapps = Get-SPWebApplication "SharePoint - intranet.company.com80"
foreach ($webapp in $webapps) 
{ 
    $webapp.AllowedInlineDownloadedMimeTypes.Add("application/pdf") 
    $webapp.Update() 
}

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)