Thursday, December 27, 2012

Using jQuery Deferred object with SharePoint Javascript Object Model

One of the common problems with using the SharePoint client object model is the asynchronous nature of the object property calls. For example, if I want to retrieve a current user’s name, I would run code something like this:

context = new SP.ClientContext.get_current();
web = context.get_web();
currentUser = web.get_currentUser();
currentUser.retrieve();
context.load(web);
context.executeQueryAsync(
Function.createDelegate(this, this.onQuerySucceeded),
Function.createDelegate(this, this.onQueryFailed)
);


The executeQueryAsync method, after it retrieves the current user information, calls the onQuerySuceeded function that then completes the processing of that request. For example, something like this would be a simple example of that callback



function onSuccessMethod(sender, args) {
var userObject = web.get_currentUser();
alert('User name:' + userObject.get_title());
}


In between the time that the executeQueryAsync is called and the callback to the onSuccessMethod function occurs any other code that was downstream executes. This makes for awkward logic and flow in client object model programs. This is especially true when building larger applications that may want to reuse, let’s say, a function called getUserInformation which returns the current user as an object. This is where a little jQuery magic can come in handy. jQuery supports using what it refers to as Deferreds. Deferreds are based on a CommonJS Promises/A design. At the bottom of this post I will provide a link to documentation on Deferreds and the CommonJS Promise/A design, but for now let’s look at an example of how we can use this.


Let’s say I wanted to build a function like I mentioned above. A function that retrieves the current user’s information and returns that as an object. Since this request is asynchronous, this could normally be very problematic, but with a Deferred object it can be done simply. First, the initial function:

 

function getUserInformation() {
var dfd = $.Deferred(function(){
context = new SP.ClientContext.get_current();
web = context.get_web();
currentUser = web.get_currentUser();
currentUser.retrieve();
context.load(web);
context.executeQueryAsync(
function(){
var userObject = web.get_currentUser();
dfd.resolve(userObject);
},
function(){
dfd.reject(args.get_message());
}
);
});
return dfd.promise();
}

This function creates a Deferred object, then it runs the same code as above to request the current user. The callback functions have been embedded into the executeQueryAsync method so that I can easily reference my existing Deferred object. Notice that the function itself does not return the user object. This is because when the function completes processing the user object has not yet returned. It will be return later asynchronously. Instead, the function returns a promise; a contract that there will be a return value in the future. When the callback does occur then the Deferred object is resolved and any code waiting for the object to return can execute. For illustration purposes here is a simple call to this function that creates a popup message box with the current user’s name (via the title property).

 

getUserInformation().done(function(user){ 
alert('User name:' + user.get_title());
});

The nice feature with this is that if later I want to return that same user’s email address I can just as easily do that reusing the same function:

getUserInformation().done(function(user){ 
alert('Email address:' + user.get_email());
});

To learn more about promises and Deferred objects visit these link

Tuesday, December 18, 2012

Using JsRender with the SharePoint JavaScript Object Model

Are you getting tired of writing a lot of JavaScript to manipulate the DOM while doing client side SharePoint development? This is simple tutorial about using templates with client side SharePoint development to avoid writing a lot of JavaScript to manipulate the DOM.  The template engine used in this tutorial is JsRender, which replaced jQuery templates.  Check out these links for more information on JsRender:

The following example was created in a content editor web part.

Create the HTML:
 
<div id="TasksTable">
<table id="taskTbl" cellpadding="5" cellspacing="5" border="1">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Status</th>
<th>Priority</th>
</tr>
</thead>
<tbody id="taskTblBody">

</tbody>
</table>
</div>


Create the Template Block:
<script id="taskTemplate" type="text/x-jsrender">
<tr>
<td>{{:#data.get_id()}}</td>
<td>{{:#data.get_item('Title')}}</td>
<td>{{:#data.get_item('Status')}}</td>
<td>{{:#data.get_item('Priority')}}</td>
</tr>
</script>
Be sure to include all the files needed:


<script 
type="text/javascript"
src="/sites/Bwarfson/CScripts/jquery-1.7.1.min.js">
</script>
<script
type="text/javascript"
src="/sites/Bwarfson/CScripts/jsrender.js">
</script>


And the JavaScript:
$(document).ready(function() {
if (typeof(_spBodyOnLoadWrapper) != 'undefined')
{_spBodyOnLoadWrapper();} //fix google chrome
ExecuteOrDelayUntilScriptLoaded(function ()
{ retrieveListItems() }, "sp.js");
});

function retrieveListItems() {
var clientContext = new SP.ClientContext.get_current();
var oList = clientContext
.get_web()
.get_lists()
.getByTitle('Tasks');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml("<View><RowLimit>500</RowLimit></View>");
this.collListItem = oList.getItems(camlQuery);
clientContext.load(
collListItem,
'Include(Id, Title, Status, Priority)'
);
clientContext.executeQueryAsync(
Function.createDelegate(this, function() {
//This is where the magic happens
$( "#taskTblBody" ).append(
$( "#taskTemplate" ).render( collListItem.get_data() )
);
}), Function.createDelegate(this, function(sender, args) {
$('.message').append('<p>Error loading tasks</p>');
$('.message').append('<p>'+args.get_message()+'</p>');
$('.message').append('<p>'+args.get_stackTrace()+'</p>');
}));

}

The final result:

image

Monday, November 5, 2012

Using JavaScript to asynchronously update a xsltlistview webpart

 

In SharePoint 2010 there is an option to “Enable Asynchronous Update”

image

This option exists on each SharePoint 2010 xsltlistview web part.  I had some custom JavaScript that was embedded in a custom action and changed list item values.  I wanted to be able to use this feature to do a no-refresh list view reload.  If you enable the “Show Manual Refresh Button” option you will see the default code that forces a refresh.  It looks something like this:

javascript: __doPostBack('ctl00$m$g_d9ea718d_b0cd_42e5_b4cf_4182334b6861$ctl02','cancel');return false;

Now I just needed a good way to get that same id that is being passed into the __doPostBack function.  Luckily, this value is similar to the GUID of the view for the list.  For example, the view guid of this webpart is {D9EA718D-B0CD-42E5-B4CF-4182334B6861}.  To build the __doPostBack I need to transform the guid to match the format of the ID.  Here is the code I wrote to perform this operation:

var selectedViewId = ctx.view;
var controlId = 'ctl00$m$g_' + selectedViewId.toLowerCase().replace(/-/g, "_").replace(/{|}/g, "") + '$ctl02';
__doPostBack(controlId);

The controlId variable is: the view guid, converted to lowercase, replacing the dashes with underscores, removing the curly braces, attaching ctl00$m$g_ to the front, and $ctl02 to the end.  This seems to work on any xsltlistview web part (whether part of a list view or embedded in a web part page). 

Monday, October 22, 2012

Creating SharePoint Custom List Columns in PowerShell

 

I needed to create a sizable test in SharePoint using a custom list.  I did not want to manually configure each column so I build a tab delimited text file that held the column configuration options and a PowerShell script to read it.  The first column of the file provides the field name, the second provides the options (they were separated by a pipe delimiter), and the third column is the field description.  I wanted all of the questions to be Radio Button choice fields so those options were hard coded.  I also wanted a letter and appended to each answer so I created an array of letters up to G. 

$w = get-spweb http://spsite
$l = $w.lists["MyTest"]

$letters = "A|B|C|D|E|F|G"
$lttr = $letters.split("|")
$spFieldType = [Microsoft.SharePoint.SPFieldType]::Choice

$file = Get-Content c:\Questions.txt

foreach ($line in $file) {
  
    $fields = $line.ToString().Split("`t")
    $fName = $l.Fields.Add($fields[0],$spFieldType,$True)   
    write-host $fName
    $f = $l.Fields[$fName]
    $choices = $fields[1].split("|")

    for ($i=0; $i -lt $choices.count; $i++)
    {
        $choiceValue = $lttr[$i] + ". " + $choices[$i]
        $f.Choices.Add($choiceValue)
    }
    $f.description = $fields[2]
    $f.EditFormat = "RadioButtons"
    $f.update()

}

The script works like a champ, and I saved myself a little time and frustration of repetitively clicking the same options over and over again.

PowerShell to display SPN and Delegation Information for SharePoint Accounts

 

In troubleshooting Kerberos issues it is sometimes helpful to see the all the SPNs and delegate to settings for my various SharePoint accounts.  Since our SharePoint accounts are named in a consistent way this ended up being quite easy.  I set a filter that looks for accounts that start with the name PRD-SP and looked in the proper container in the Active Directory.  I piped the output to a file and I had a useful listing of all the SPNs and their delegate to settings.

$strFilter = "(&(objectCategory=User)(sAMAccountName=PRD-SP*))"

$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = "LDAP://OU=SharePoint,OU=Special Accounts,DC=domain,DC=com"
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"

$colProplist = "sAMAccountName","name","msDS-AllowedToDelegateTo","servicePrincipalName"

foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}

$colResults = $objSearcher.FindAll()

foreach ($objResult in $colResults) {
    #$objResult.Properties
    $objResult.Properties["name"]
    $objResult.Properties["samaccountname"]
    "================================================"
    "msDS-AllowedToDelegateTo"
    "------------------------------------------------"
    $objResult.Properties["msds-allowedtodelegateto"]
    "------------------------------------------------"
    "servicePrincipalName"
    "------------------------------------------------"
    $objResult.Properties["serviceprincipalname"]
    ""
    ""
}

Delete a Retention Information Management Policy that is Corrupt

 

We had a series of content type retention polices that for some reason ended up being corrupt.  When I would try to edit the policy through the user interface I would get an error:

image

No matter what I did through the user interface I would get that error.  To remedy this I wrote a little 4 line PowerShell script that deletes the policy.  The script accepts 3 arguments (site URL, list name, and content type). 

$w = Get-SPWeb $args[0]
$l = $w.lists[$args[1]]
$c = $l.ContentTypes[$args[2]]
[Microsoft.Office.RecordsManagement.InformationPolicy.Policy]::DeletePolicy($c)

The script deletes the policy and then the policy can be reconfigured through the site settings.

Force a Single User’s Profile to Sync With Active Directory

 

Occasionally we will have a user that changes their last name and wants it to reflect the change across SharePoint.  In order to aid this process I wrote this script to force a resynchronize of each instance of the user profile in the user information lists.  The script accepts one argument, which is the username to resync (“DOMAN\User”).

$webapps = Get-SPWebApplication
foreach ($webapp in $webapps) {
    [string] $login = $args[0]
    $sites = get-spsite -Limit All -WebApplication $webapp
    foreach ($s in $sites) {
        write-host $s.url
        $w = $s.RootWeb;
        $u = get-SPUser -Web $w -limit all | Where-Object {$_.userlogin -eq $login}
        if ($u -ne $null) {
            write-host "`t$($w.url)"
            Set-SPUser $u -SyncFromAD
            write-host "`tUpdated"
        }
    }
}

Showing left navigation for entire library of web part pages

 

I had a request to make a whole library full of web part pages show the left navigation.  These pages had been created and populated with web parts previously.  Instead of editing the template on each of these pages manually, I decided to script it using PowerShell. 

$w = get-spweb “http://sharepoint/sites/collection/subweb”
$f = $w.RootFolder.SubFolders["Pages"]
foreach ($p in $f.Files) {
    $enc = [system.Text.Encoding]::ASCII
    $b = $p.OpenBinary()
    $str = $enc.GetString($b)
    $new = $str -replace "<ContentTemplate>(.|\n)*</ContentTemplate>","<ContentTemplate></ContentTemplate>"
    $new = $new -replace "<asp:Content.*PlaceHolderLeftNavBar.*/asp:Content>",""
    $new = $new -replace "<asp:Content.*PlaceHolderLeftActions.*/asp:Content>",""
    $new = $new -replace "^\?\?\?",""
    $p.SaveBinary($enc.GetBytes($new))
}

The script opens each file in the “Pages” library that housed the web part pages and strips out the code that hides the left navigation.

Friday, October 19, 2012

Embedding Javascript in Custom Actions

 

SharePoint 2010 added the ability to tie custom actions to a list or list item:

image

Custom Actions can be added to these menus/ribbons:

image

These custom actions can start workflows, navigate to a form, or navigate to a URL.  This gives some limited customization that can be do to how a list behaves, and what actions can be taken, but with little creativity this functionality can be extended.

The first thing that needs to be understood to extend this functionality is that there a some key tokens that can be used with building URLs.  These tokens are:

  • {ItemId} – ID (GUID) taken from the list view.
  • {ItemUrl} – Web-relative URL of the list item (Url).
  • {SiteUrl} – The fully qualified URL to the site (Url).
  • {ListId} – ID (GUID) of the list (ID).
  • {ListUrlDir} – Server-relative URL of the site plus the list's folder.
  • {Source} – Fully qualified request URL.
  • {SelectedListId} – ID (GUID) of the list that is currently selected from a list view.
  • {SelectedItemId} – ID of the item that is currently selected from the list view.

Source: <http://msdn.microsoft.com/en-us/library/ff458385.aspx>

The other key piece of information needed is that inside the URL can instead be a Javascript function reference like this:

image

Then all that is needed is to create a script library with that function and either reference it on the page (List View), the masterpage, or in the SPWeb property: CustomJavaScriptFileUrl.  For my example I used the following code to create a function that will update any value in the target list item.  The parameters are:

  1. Item ID
  2. List ID
  3. Column Name
  4. New Column Value

The finished Javascript looks like this:

function updateListItem(ItemId, ListId, FieldName, FieldValue) {
    var clientContext = new SP.ClientContext.get_current();
    var oList = clientContext.get_web().get_lists().getById(ListId);
    var oListItem = oList.getItemById(ItemId);
    oListItem.set_item(FieldName, FieldValue)
    oListItem.update();
    clientContext.load(oListItem);
    clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}


function onQuerySucceeded() {
    window.location.reload(false);
}

function onQueryFailed(sender, args) {
    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}

With just a little effort now I have a list action that dynamically completes the list item without the overhead of a workflow or making the user open and edit the item:

image

image

SharePoint 2010 CSS Classes

 

SharePoint 2010 includes a series of CSS Classes that allow you to consume the theme colors.  This allows you to have custom built HTML content that dynamically recolors with the SharePoint theme:

The following table shows the background color CSS classes and their corresponding color on the default theme:

CSS Class Color

ms-rteThemeBackColor-1-0

#FFFFFF

ms-rteThemeBackColor-2-0

#000000

ms-rteThemeBackColor-3-0

#F5F6F7

ms-rteThemeBackColor-4-0

#182738

ms-rteThemeBackColor-5-0

#0072BC

ms-rteThemeBackColor-6-0

#EC008C

ms-rteThemeBackColor-7-0

#00ADEE

ms-rteThemeBackColor-8-0

#FD9F08

ms-rteThemeBackColor-9-0

#36B000

ms-rteThemeBackColor-10-0

#FAE032

These match up exactly with the 10 theme colors:

image

There is a similar set of CSS tags to match the fore color to the theme colors.  Following the same pattern:

CSS Class Color

ms-rteThemeForeColor-1-0

#FFFFFF

ms-rteThemeForeColor-2-0

#000000

ms-rteThemeForeColor-3-0

#F5F6F7

ms-rteThemeForeColor-4-0

#182738

ms-rteThemeForeColor-5-0

#0072BC

ms-rteThemeForeColor-6-0

#EC008C

ms-rteThemeForeColor-7-0

#00ADEE

ms-rteThemeForeColor-8-0

#FD9F08

ms-rteThemeForeColor-9-0

#36B000

ms-rteThemeForeColor-10-0

#FAE032

There is also a way to get a few shades lighter and darker of each theme color.  The image below demonstrates this for the 10th theme color:

image

There are also classes to change the font face:

image

and font size:

image

Another great resource for other themed CSS classes can be found here:

http://silverpointsoftware.wordpress.com/themed-css-classes/

Microsoft SharePoint Patterns and Practices Logging Provider

 

Part of the patterns & practices SharePoint guidance published by Microsoft is a framework for doing logging (http://spg.codeplex.com/).  The DLL provided (Microsoft.Practices.SharePoint.Common.dll) includes logger class that allows you to log messages easily to the ULS.  The one challenge is that it registers custom logging areas to accomplish this.  We were struggling to get one of these areas registered because the .NET feature trying to register the area did not have the proper privileges at runtime.  To avoid giving the feature the required farm wide permissions it needed, I instead opted to register the logging area via PowerShell.  Two PowerShell commands later, voilĂ :

PS C:\> [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Practices.SharePoint.Common")

GAC Version Location
--- ------- --------
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\Microsoft.Practices.SharePoint.Common\2.0.0.0__ef4330804b3c4129\Microsoft.Practices.SharePoint.Common.dll

PS C:\> [Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService]::Register()

Thursday, April 26, 2012

Customize Search Results with Custom Links

Adding: View Properties, Edit Document, Show Folder, Permalink, and Email Link

A request came my way to add some custom links to the search results of a site collection.  They wanted to view the properties of documents straight from the search results.  There are a few good blogs posts on this topic, and much of what I will share is derived from their good work.  
Once I got started adding the "View Properties" link.  I realized it would be cool to have a few more links for actions such as:
  • Edit Document
  • Show Folder
  • Permalink
  • Email Link

Step 1: Adding Managed Properties to Search

Anne Stenberg has a great article on the "Basic" property category.  I leveraged her information in building my custom links.  Her article can be found at:

To add the needed managed properties open the Farm Search Administration and select your search service application; then, click on the Metadata Properties.  We are going to need to add a couple to do what we need.

The first metadata property we need to add I called ItemID.  This property maps to the List Item "ID" column found in every SharePoint list.  This property will be used to create the URL for the "View Property" link.

The second metadata property need is a property I called host.  This maps to the Basic:4 property  

The third metadata property needed is my "Permalink" property which is the Document link created as part of the Document ID Service feature (read more: http://msdn.microsoft.com/en-us/library/ee559302.aspx).

These new properties will not be present in the search results until the next full crawl.  You can confirm if they are attached to a given result by viewing the search results XML (http://msdn.microsoft.com/en-us/library/ms546985.aspx).

Step 2: Customizing the Search Core Results web part columns.

If you edit the results.aspx file in a search center site, you will find the primary web part on the page is called Search Core Results.  This web part is easily customizable.  We are going to be changing the properties under the Display Properties section.  In order to customize the look and feel of the results the Use Location Visualization check-box must be unchecked.  

You can then modify the Fetched Properties text-box.  We need to add our three new properties to the list so that the web part can consume them.  Each column is declared by an XML tag that looks like this:

 <Column Name="Title"/>  

To add our three new columns we just add them using the same format and end up with a list of Properties:

 <Columns> <Column Name="WorkId"/> <Column Name="Rank"/> <Column Name="Title"/> <Column Name="Author"/>   
 <Column Name="Size"/> <Column Name="Path"/> <Column Name="Description"/> <Column Name="Write"/>   
 <Column Name="SiteName"/> <Column Name="CollapsingStatus"/> <Column Name="HitHighlightedSummary"/>   
 <Column Name="HitHighlightedProperties"/> <Column Name="ContentClass"/> <Column Name="IsDocument"/>   
 <Column Name="PictureThumbnailURL"/> <Column Name="PopularSocialTags"/> <Column Name="PictureWidth"/>   
 <Column Name="PictureHeight"/> <Column Name="DatePictureTaken"/> <Column Name="ServerRedirectedURL"/>   
 <Column Name="ItemID"/> <Column Name="Host"/> <Column Name="Permalink"/> </Columns>  

Step 3: Customizing the XSL.

The other part we will be customizing is the XSL that controls how the XML search results are rendered.  I am including my XSL code blocks below, but first I will explain what they do.

The first block is added to the results body and is placed just below the result metadata.  This block of code calls a series of custom XSL templates.  Each template renders one of the links.  I have named the templates in a way that it should be self-evident which link they render.  I pass to these templates the parameters needed to render the link (notice, our custom managed properties are referenced, as well as a few default managed properties).  To implement this code, add the following lines to your XSL (using the XSL Editor) below the div tag with the "srch-Metadata2" class:

 <div class=”srch-Metadata2″>  
   <xsl:call-template name=”ViewProperties”>  
    <xsl:with-param name=”Url” select=”url” />  
    <xsl:with-param name=”itemID” select=”itemid” />  
    <xsl:with-param name=”isDocument” select=”isdocument” />  
    <xsl:with-param name=”host” select=”host” />  
   </xsl:call-template>  
   
   <xsl:call-template name=”EditDocument”>  
    <xsl:with-param name=”Url” select=”url” />  
    <xsl:with-param name=”contentclass” select=”contentclass” />  
   </xsl:call-template>  
   
   
   <xsl:call-template name=”ShowFolder”>  
    <xsl:with-param name=”Url” select=”sitename” />  
    <xsl:with-param name=”itemID” select=”itemid” />  
   </xsl:call-template>  
   
   <xsl:call-template name=”ShowPermalink”>  
    <xsl:with-param name=”Permalink” select=”permalink” />  
    <xsl:with-param name=”contentclass” select=”contentclass” />  
   </xsl:call-template>  
   
   <xsl:call-template name=”EmailLink”>  
   <xsl:with-param name=”Permalink” select=”permalink” />  
   <xsl:with-param name=”Url” select=”urlEncoded” />  
   </xsl:call-template>  
 </div>  

The second block of code is the templates.  The most complicated of these templates is the ViewProperties template.  I leaned heavily on the work of other people to build this template and I suggest you visit their sites linked above.  The sites above did have some bugs in their template and so I have made modifications to compensate for those bugs (namely the template did not generate the correct link for a document library with folders).  The rest of the templates are much more straight forward.  To add these templates to the XSL, just before the line that says "<!-- XSL transformation starts here -->" place the following code:

 <xsl:template name="ViewProperties">  
  <xsl:param name="Url" />  
  <xsl:param name="itemID" />  
  <xsl:param name="host" />  
  <xsl:param name="isDocument" />  
  <xsl:if test="string-length($itemID) > 0">  
     <xsl:variable name="UrlLcase" select="translate($Url, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>  
     <xsl:variable name="hostLcase" select="translate($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>  
     <xsl:choose>  
    <xsl:when test="$isDocument = 'True'">  
        <xsl:variable name="docLib" select="substring-before(substring-after(substring-after($UrlLcase,$hostLcase), '/'), '/')" />  
        <xsl:variable name="viewPropUrl" select="concat($host, '/', $docLib, '/Forms/DispForm.aspx?id=', $itemID)" />  
        <a href="{$viewPropUrl}">View Properties</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:when>  
    <xsl:when test="$UrlLcase = $hostLcase">  
    </xsl:when>  
    <xsl:otherwise>     
        <xsl:variable name="listLoc" select="substring-before(substring-after(substring-after($UrlLcase,concat($hostLcase,'/lists')), '/'), '/')" />  
        <xsl:variable name="viewPropUrl" select="concat($host, '/lists/', $listLoc, '/DispForm.aspx?id=', $itemID)" />  
        <a href="{$viewPropUrl}">View Properties</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:otherwise>  
    </xsl:choose>  
  </xsl:if>  
 </xsl:template>  
   
 <xsl:template name="EditDocument">  
   
    <xsl:param name="Url" />  
    <xsl:param name="contentclass" />  
    <xsl:if test="$contentclass='STS_ListItem_DocumentLibrary'">  
       <a href="{$Url}" onclick="return DispEx(this,event,'TRUE','FALSE','FALSE','SharePoint.OpenDocuments.3','0','SharePoint.OpenDocuments','','','','1','0','0','0x7fffffffffffffff')">Edit Document</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:if>  
 </xsl:template>  
   
 <xsl:template name="ShowFolder">  
    <xsl:param name="Url" />  
    <xsl:param name="itemID" />  
    <xsl:if test="string-length($itemID) > 0">  
       <a href="{$Url}">Show Folder</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:if>  
 </xsl:template>  
   
 <xsl:template name="ShowPermalink">  
    <xsl:param name="Permalink" />  
    <xsl:param name="contentclass" />  
    <xsl:if test="string-length($Permalink) > 0">  
       <a href="{$Permalink}">Permalink</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:if>  
 </xsl:template>  
   
 <xsl:template name="EmailLink">  
    <xsl:param name="Permalink" />  
    <xsl:param name="Url" />  
    <xsl:choose>  
    <xsl:when test="string-length($Permalink) > 0">  
       <a href="mailto:?body={$Permalink}">Email Link</a>  
    </xsl:when>  
    <xsl:otherwise>  
       <a href="mailto:?body={$Url}">Email Link</a>  
    </xsl:otherwise>  
    </xsl:choose>  
 </xsl:template>  

Last Thoughts: 

Once you have made the changes in the XSL Editor, apply the changes and save the page.  You should now see results that have an extra line of links like this:



The templates will confirm that the different links apply to each result.  I have found a few minor bugs (for example on mysites the View Properties link still sometimes does not work).  The one thing I would like to add is a source parameter that gets passed in the View Properties link.  This would ensure that if the user clicks close that SharePoint would return to the results page.  If I get it working I will post the tweak.

Tuesday, April 24, 2012

Hiding the First Top-Link Tab for a Publishing SharePoint Site

After enabling the SharePoint Server Publishing Infrastructure feature on a SharePoint site the first tab in the top-link bar automatically changes to the site title:

Here is an example of a site before the publishing infrastructure has been enabled:

This is what the site looks like after enabling the feature (I also changed the site title to further illustrate the problem):

This can be a very undesirable result when the site title is long or just undesirable as the first tab's label.  With a few masterpage changes this behavior can be remedied.  To accomplish this follow these steps:

Open the site in SharePoint Designer and go to the Master Pages left navigation. 
Make a copy of the "v4.master" and rename it to something like "modified.master":


The first step is to edit your new "modified.master" and add the following line to the Registers at the top of the master page:

<%@ Register 
 Tagprefix="PublishingNavigation" 
 Namespace="Microsoft.SharePoint.Publishing.Navigation" 
 Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" 
%>


Further down in the master page you will find a block of code that looks something like this:

The part of the code that we are going to focus on is the SharePoint:DelegateControl.  A good article that describes the purpose of the DelegateControl can be found here: http://msdn.microsoft.com/en-us/library/ms432695.aspx.  We are going to replace this delegate control with a control that we can customize a little bit more.  Highlight the SharePoint:DelegateControl (like shown below) and delete it:

We are going to replace the deleted text with the following:

<PublishingNavigation:PortalSiteMapDataSource 
ID="topSiteMap" 
Runat="server" 
SiteMapProvider="CombinedNavSiteMapProvider" 
StartFromCurrentNode="true" 
StartingNodeOffset="0" 
ShowStartingNode="false" 
TrimNonCurrentTypes="Heading" 
/>

This creates a new TopNav datasource.  This data source will still consume the out of the box data navigation, but will not show the first tab (created by the publishing feature).

After making this modification the top link bar the top link now looks like this:

For more information on this topic check out these other blogs:

http://blogs.msdn.com/b/ecm/archive/2007/02/10/moss-navigation-deep-dive-part-1.aspx


Wednesday, April 11, 2012

How To: Enable Embedded HTML in a Calculated Column

We had implemented the calculated column to HTML JavaScript that can be found on the Path to SharePoint website (http://blog.pathtosharepoint.com/2008/09/01/using-calculated-columns-to-write-html/).   This was a great solution at the time, and I really appreciate the work they did over at Path to SharePoint to create this method.

This solution had worked well, but in order to make it work across the entire site the JavaScript had been embedded in the Master Page on each site that used a calculated column to display HTML.  We were using the calculated column to create a status indicator on projects, and the column was named the same thing on each site.  Luckily, Microsoft has provided a better way for this scenario.

Microsoft has a great article on using XSLT style sheets to customize the rendering of list views (http://msdn.microsoft.com/en-us/library/ff606773.aspx).  By creating an XSLT style sheet for specific column you can change how that column is rendered.  In the use-case above the column could be customized by creating a new fldtypes_*.xsl file.  A fldtypes.xsl file customizes the way that SharePoint renders a specific column type.  It can also be used to customize how SharePoint renders a column of a specific name.

Let's look at an example:
I created a file called fldtypes_ScenicSP.xsl and placed it in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\XSL on my web front end.  Inside this file I placed the following style sheet:


<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema"
                xmlns:d="http://schemas.microsoft.com/sharepoint/dsp"
                version="1.0"
                exclude-result-prefixes="xsl msxsl ddwrt"
                xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
                xmlns:asp="http://schemas.microsoft.com/ASPNET/20"
                xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:SharePoint="Microsoft.SharePoint.WebControls"
                xmlns:ddwrt2="urn:frontpage:internal">


  <xsl:template match="FieldRef[@Name='Status_x0020_Indicator']" mode="Text_body">
    <xsl:param name="thisNode" select="."/>
    <xsl:value-of select="$thisNode/@*[name()=current()/@Name]" disable-output-escaping ="yes" />
  </xsl:template>
</xsl:stylesheet>

In the style sheet I have a match condition of FieldRef[@Name='Status_x0020_Indicator'].  This indicates that I want this style sheet to apply to all fields that are named "Status Indicator".  You need to be careful not to pick a field name that is going to be commonly used for a lot of different tasks.  This basically makes that field name reserved as a special field that will be treated by SharePoint differently than any other field.  The behavior I changed is to add the following: disable-output-escaping ="yes".  This tells SharePoint that when it renders that field to render the HTML instead of displaying the tags.  The select clause in the value-of is explained in much more detail on the Microsoft article posted above (which I highly encourage you to read so that you understand this principle).

Once I save my file I then need to recycle the application pool on any web application where I intend on using this new style sheet (I suggest doing an IISReset so that all application pools are cycled).  This same file will need to be copied to each web front end and the application pool will need to be cycled on each as well.  Once this has been done you can now see the fruits of your labor.

First, create a task list (I called mine myTasks):

Second, I added a column called Indicator.  I created it as a choice column with three choices (red, yellow, or green). I also created a record

Third, I created a calculated column that generates an image URL based on the Indicator color.

Finally, I added one more calculated column named Status Indicator that creates an image tag.  Status Indicator was the column above for which we changed the rendering behavior.

The finished product can be seen below:


This was a very simple example and can be replicated using an out of the box KPI list, but I will post some more useful examples in a second article on this topic. Please make sure you read the Microsoft article above.  They do a great example of making negative values turn red on a currency column.