Show Workflow history in Workflow status column in list view

In this post i will show you how to display the workflow history log in the workflow status column instead of just “In Progress” or “Completed”.

The default behavior is like this:
IMG

The status is “Completed”, but what did it do? A click on “Completed” brings up the workflow history log:
IMG

I think that is to complicated!

How about we display it like this:
IMG
Of course it is fully clickable, and takes you right to the good old workflow status page.

Here’s how it’s done

The list:
IMG

The workflow:
IMG
IMG

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG
The code for the file “WorkflowHistoryInListView.js” is supplied below.

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

The scripts “interaction.js” and stringBuffer.js” is created by Erucy and published on codeplex – you can find them here.

Add a CEWP below your ListView like this:
IMG

With this code:

<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/interaction.js"></script>
<script type="text/javascript" src="/test/English/Javascript/stringBuffer.js"></script>
<script type="text/javascript">
   WfListGuid = "{49293528-0c47-4150-b70c-77e876548d12}"; 
</script>
<script type="text/javascript" src="/test/English/Javascript/WorkflowHistoryInListView.js"></script>

You could also use WfListGuid = “Workflow History”; for a english language site – but remember that the displayname of your workflow history list will change depending of your site’s language. I personally always use list GUID.

Note! The workflow history list is only present in a site if there is at least one workflow configured. If it’s not present in your site – it will be created with your first workflow. All subsites has it’s own workflow history list.

The list GUID for your Workflow history list is found like this:
Browse to your list by its “displayname” – Workflow History for a english site (it’s hidden from browser’s and do not display in “View all site content”). When in doubt – use SharePoint designer to get the name of your list among the other lists in the “Lists-folder”.

When you have browsed to the list – right click and view source. Search for “listName” and you will find it like this:
ctx.listName = “{49293528-0C47-4150-B70C-77E876548D12}”;

Here’s the sourcecode for the file “WorkflowHistoryInListView.js”:

/* Display the "Workflow History" instead of just "In Progress" or "Completed" in the Workflow-status column
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * Version: 1.0
 * LastMod: 01.10.2009
 * ---------------------------------------------
*/

listGuid = ctx.listName; // SharePoint provides this

WriteLogg();
function WriteLogg(){
if(typeof(wfListObj)=="undefined")wfListObj = getWfLogg(); // If not already createt - build an "object" containing all WF-history for the current list
$('a[href*="WrkStat.aspx"]').each(function(){ // Find all a-tags that have a href containing WfkStat.aspx
	if($(this).text()!=''){	
		var wfGuidRaw = $(this).attr('href'); // Get the href
		var wfGuid = unescape(wfGuidRaw.substring(wfGuidRaw.lastIndexOf('WorkflowInstanceID=')+19)).toLowerCase(); // Get the GUID of the current WF from the href
		var wfLogg = wfListObj[wfGuid] // Get the actual history from the WF-object by "asking" for the log on this list items GUID
		if(wfLogg!=undefined){ // If the workflow history isn't empty
			wfLogg = wfLogg.split('|').join('<br>'); // Separate the lines		
			$(this).html(wfLogg); // Set the clickable part of the a-tag to the actual history from the log
		}	
	}
});
}

function getWfLogg(){
wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/'; // Set the path to the webservice "lists.asmx"
var query = "<Where><Eq><FieldRef Name='List' /><Value Type='Text'>" + listGuid + "</Value></Eq></Where>"; // Get all WF-history for the current list	
	var res = queryItems(WfListGuid,query,['ID','WorkflowInstance','Description']); 
	obj = {};
		if(res.count == -1){
			alert("An error occured in the query:n" + query); // On error
		}else{
			$.each(res.items,function(idx,item){
				if(item['Description']!=null){
					// Is there history already logged on this GUID?
					if(obj[item['WorkflowInstance']]==undefined){ 
						// No
						obj[item['WorkflowInstance']] = item['Description']; 
					}else{
						// Yes - add to it so that all log on current WF-run is displayed together
						obj[item['WorkflowInstance']] = obj[item['WorkflowInstance']] + "|" + item['Description']; 
					}
				}	
			});
			return obj;	// Return object containing all WF-history on current list	
		}	
}

// Attaches a call to the function to the "expand grouped elements function" for it to function in grouped listview's
function ExpGroupRenderData(htmlToRender, groupName, isLoaded){
	var tbody=document.getElementById("tbod"+groupName+"_");
	var wrapDiv=document.createElement("DIV");
	wrapDiv.innerHTML="<TABLE><TBODY id="tbod"+groupName+"_" isLoaded=""+isLoaded+"">"+htmlToRender+"</TBODY></TABLE>";
	tbody.parentNode.replaceChild(wrapDiv.firstChild.firstChild,tbody);
WriteLogg(); // Call the script - the rest of the function "ExpGroupRenderData" is a unmodified SharePoint function from the file "BFORM.JS"
}

That’s it!

Feel free to ask if something is unclear.
Alexander

Prevent editing of a list item if the workflow has failed

11.01.2010: A follow-up on this article is posted here: Check workflow status and refresh page when status equals “Completed”


Have you ever wanted to prevent editing of a list item if the workflow has failed? Here is the answer.

This code queries the list trough the web-service lists.asmx to get the status of the workflow. If the status i “3” – the workflow has failed and we want to prevent further editing of the element until the issue is resolved (the workflow is aborted and the cause is identified).

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

The scripts “interaction.js” and stringBuffer.js” is created by Erucy and published on codeplex – you can find them here.

Find the “FieldInternalName” of your workflow:
IMG

IMG

Find your list’s GUID and edit the script below and change the listGuid (line 18) and the “FieldInternalName” of your “Workflow-column” (line 18 and 20).

Add a CEWP below your EditForm like this:
IMG

With this code: (change listGuid and Wf’s “FieldInternalName”)

<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/interaction.js"></script>
<script type="text/javascript" src="/test/English/Javascript/stringBuffer.js"></script>
<script type="text/javascript">
var SiteTitle = $.trim($(".ms-sitetitle a").text());
var ListName = $.trim($(".ms-pagetitle a").text()); 

var wfStatus = getWorkflowStatus();

if(wfStatus==3){ // 3 = "Error Occured"
	$("#part1").hide(); // Hide the list form
	$("#part1").before("<div>A workflow has failed. You cannot edit this item until this issue is resolved.</div><div><a title='Click to send e-mail to an administrator' href='mailto:alexander.bautz@gmail.com?subject=Failed workflow on site: " + SiteTitle + ", list: " + ListName + ", itemId: " + getID() + "'>Click to send e-mail to an administrator</a></div>");
}

function getWorkflowStatus(){
var thisID = getID();
	wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/';
    var item = getItemById('{d3d26e2b-93ca-4981-bf02-28dc73ad9287}', thisID, ['SampleWo']); // Change ListGuid and Workflow "FieldInternalName"
    if(item != null){ 
        return  item['SampleWo'];    
    }
}

function getID() {
var ID = '';
var end = window.location.search.indexOf('&');
	if(window.location.search.indexOf('&')<0){
		ID = window.location.search.substring(4);
	}else{		
		ID = window.location.search.substring(4,end);		
	}
	return ID;
}
</script>


If your workflow status is “3”, your EditForm looks like this:

IMG

A click on the link opens the default e-mail program and the subject is prefilled like this:
IMG

Please ask if something is unclear.

Alexander

Convert Singleline textfield to filtered lookup dropdown

08.04.2011 A small makeover to get rid of some extra script references and to add compatibility to all major browsers.


This one is related to my post on Cascading dropdowns, but is used to convert one column of type “Single line of text” or a column of type “Hyperlink or Picture” to a filtered dropdown.

You can populate this dropdown from any list in current site, sub site or parent site – as long as the user has read access to the list holding the information. The lookup can be against all elements – or filtered by any metadata in the source list item – like an “active/inactive” – Yes/No Checkbox-column.

In your list – add a column of type “Single line of text”, with a nice “FieldInternalName” (a name without spaces and special characters) – you can edit the column name as soon as the column is created to get a readable “DisplayName”. It is this newly created “Single line of text-column” that is to be converted to a dropdown. by this script. In this example I have used the “Title-column” as the field to convert.

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). Make sure all users have read access to that folder.

Download the file “dropdownFromTextOrHyperlinkField.js” from here

Upload it to the selected folder.

Add a CEWP below your NewForm list-form (and EditForm if you like) like this:

IMG
With this code:

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/dropdownFromTextOrHyperlinkField.js"></script>
<script type="text/javascript">
fields = init_fields_v2();

singleFilteredDropdown('{1b128964-7075-491a-be7b-55c40d94714b}',L_Menu_BaseUrl,'Title','Active','Boolean','Eq','1',false,'DropdownNr1','<select>','Title',true,false,false);

</script>

Please note that the call to “init_fields_v2()” has changed from “init_fieldInternalName()” from earlier versions. You have to change the “src” to the file “dropdownFromTextOrHyperlinkField.js” to match your local path.

The list that is source for my lookup has a listGuid of {1b128964-7075-491a-be7b-55c40d94714b}, a Yes/No column named Active, and the Title column which holds the value to populate the dropdown.

IMG

The source list is located in the same site as the target list – hence the variable L_Menu_BaseUrl which SharePoint provides for us.

You should end up with a result like this:
IMG
Here some of the elements in the sourcelist is set to “inactive”:
IMG

To get a hyperlink back to the selected element – use a column of type “Hyperlink or Picture” – with hyperlink format, and change the parameter “showAsURL” to true.

Please ask if something is unclear.

Alexander

Showing or hiding list fields based on membership in a SharePoint group

05.07.2012 An updated version can be found here


This article describes how to show or hide a form field based upon membership or not membership in a SharePoint group.

This solution uses the script created in this article to access the user info on the current user. It is a precondition that you read the previous article before continuing with this one.

As always we begin like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a subsite named “test” with a subsite named “English” with a document library named “Javascript”):
IMG

I use some code (”interaction.js” and stringBuffer.js”) created by Erucy and published on codeplex.

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

The file “AccessUserProfileInWSS.js” is created in the previous article.

The sourcecode for the IsUserInGroup.js” looks like this:

/* isUserInGroup - Used to check if a user is in a spesific SharePoint-group
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * LastMod: 20.09.2009
 * ---------------------------------------------

Use:
 * userId : Only supplied if the user to query is not the logged in user.
 * groupId : ID of the group to check membership in
 * returnGroupName (true) : Returns the groupName of the group if the user is in it - returns false if user is not in the group.
 * returnGroupName (false) : Returns &quot;true&quot; or &quot;false&quot;
 *
 * Refer these scripts:
 *  interaction.js // Erucy - http://spjslib.codeplex.com
 *  stringBuffer.js // Erucy - http://spjslib.codeplex.com
 *  jQuery // http://jQuery.com
 *  AccessUserProfileInWSS.js // https://spjsblog.com/2009/09/20/accessing-user-profile-information-in-wss-3-0-with-javascript/
*/

function isUserInGroup(UserId,groupId,returnGroupName){
if(UserId=='')UserId = _spUserId;
var ui = getUserInfo(UserId); 
var userLoginName = ui['Name'];
var ug = getGroupCollectionFromUser(userLoginName);
	for(i=0;i&lt;ug.length;i++){
	var id = ug[i].split('|');
		if(id[0]==groupId){
			if(returnGroupName){
				return id[1];
			}else{
				return true;
			}				
		}
	}
return false;
}

function getGroupCollectionFromUser(userLoginName){
	var result = [];
	innerPost(wsBaseUrl + 'usergroup.asmx', 
		'http://schemas.microsoft.com/sharepoint/soap/directory/GetGroupCollectionFromUser',
		'&lt;GetGroupCollectionFromUser xmlns=&quot;http://schemas.microsoft.com/sharepoint/soap/directory/&quot;&gt;&lt;userLoginName&gt;' + userLoginName + '&lt;/userLoginName&gt;&lt;/GetGroupCollectionFromUser&gt;',
		function(data){		
			$('Group', data).each(function(idx, itemData){
				result.push($(itemData).attr('ID') + &quot;|&quot; + $(itemData).attr('Name'));
			});
		});
	return result;
}

function init_fields(){ // Modified version og the function created by Erucy - http://spjslib.codeplex.com/
  var res = {};
  $(&quot;td.ms-formbody&quot;).each(function(){
	  if($(this).html().indexOf('FieldInternalName=&quot;')&lt;0) return;
	  var start = $(this).html().indexOf('FieldInternalName=&quot;')+19;
	  var stopp = $(this).html().indexOf('FieldType=&quot;')-7;
	  var nm = $(this).html().substring(start,stopp);
	  res[nm] = this.parentNode;
  });
  return res;
}

Save this as a text file and rename to “IsUserInGroup.js”, then upload to the library as shown above.

Then you add a CEWP below the list form in NewForm (and if you like – DispForm and EditForm) with this sourceCode:
IMG

CEWP sourcecode:

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/interaction.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/stringBuffer.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/AccessUserProfileInWSS.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/IsUserInGroup.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
fields = init_fields();
var arrToShowOnlyForOwnerGroup = ['OnlyForOwnerGroup1','OnlyForOwnerGroup2']; // FieldInternalNames
var isInGroup = isUserInGroup('',38,false)
if(!isInGroup){
	for(i=0;i&lt;arrToShowOnlyForOwnerGroup.length;i++){
		$(fields[arrToShowOnlyForOwnerGroup[i]]).hide();
	}
}
&lt;/script&gt;

The variable “arrToShowOnlyForOwnerGroup” is an array of “FieldInternalNames” of the fields to hide for “non owners”. Look here for a quick guide for obtaining the “FieldInternalName” of your fields.

“38” is the ID of the group “Owners”. You find your group ID by looking at the URL under Site Actions > Site settings > People and Groups > your group – look at the URL:
/_layouts/people.aspx?MembershipGroupId=38.

For group members the NewForm looks like this:
IMG

For all others the NewForm looks like this:
IMG

Just remember not to set the field as required from SharePoint UI – if the user cannot see the field he can not fill it! To learn how to add dynamic required fields – se fieldutility.js from Erucy on codeplex.

Alexander

Accessing user profile information in WSS 3.0 with javascript

18.09.2011 I have posted a new solution which makes this one obsolete. You find the new one here


09.09.2010 Updated the function “getUserInfo”. It no longer requires the list GUID for the user list to be specified in the script.

31.10.2009: Small update for default picture in the example CEWP.

This article describes how to access the “user profile” under WSS 3.0 via javascript. The method used is a query trough the webservice lists.asmx.

I use some code (“interaction.js” and stringBuffer.js”) created by Erucy and published on codeplex.

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

As always we begin like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a subsite named “test” with a subsite named “English” with a document library named “Javascript”):
IMG

You can call this script from any page. In this example i will place it on Default.aspx.

The sourcecode for the “AccessUserProfileInWSS.js” looks like this:

/* getUserInfo - Returns &quot;user info&quot; data from user list in WSS 3.0 (not MOSS user profile)
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * LastMod: 20.09.2009
 * ---------------------------------------------

Refer these scripts:
 interaction.js // Erucy - http://spjslib.codeplex.com
 stringBuffer.js // Erucy - http://spjslib.codeplex.com
 jQuery // http://jQuery.com

Use:
 var ui = getUserInfo(); // If UserId is not specified it assumes it's logged in user (_spUserId)
 alert(ui.Title);
*/

function getUserInfo(UserId){
wsBaseUrl = '/_vti_bin/';
var uiObj = {};
if(typeof(UserId)==&quot;undefined&quot; || UserId=='')UserId = _spUserId;
var arrOfFields = ['ID', 'Name', 'Title', 'EMail', 'Department', 'JobTitle', 'Notes', 'Picture',
'IsSiteAdmin', 'Created', 'Author', 'Modified', 'Editor', 'SipAddress', 'Deleted'];
var item = getItemById('UserInfo',UserId,arrOfFields);
    if(item != null){
	    for(i=0;i&lt;arrOfFields.length;i++){
	    	if(item[arrOfFields[i]]!=null){
	    		uiObj[arrOfFields[i]] = item[arrOfFields[i]];
	    	}else{
	    		uiObj[arrOfFields[i]] = '';
	    	}
	    }
       	return uiObj;
    }else{
        for(i=0;i&lt;arrOfFields.length;i++){
    		uiObj[arrOfFields[i]] = &quot;User with id &quot; + UserId + &quot; not found.&quot;;
    	}
		return uiObj;
	}
}

Save this as a text file and rename to “AccessUserProfileInWSS.js”, then upload to the library as shown above.

You call the script from a standard CEWP like this:
IMG

Sourcecode for CEWP:

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/interaction.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/stringBuffer.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/AccessUserProfileInWSS.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;

var ui = getUserInfo(); // If UserId is not specified it assumes it's logged in user (_spUserId)
picSrc = '/_layouts/images/person.gif';
if(ui.Picture!=''){
picSrc = ui.Picture.split(', ')[0]
}
var str = '';
str += &quot;Accountname: &quot; + ui.Name + &quot;&lt;br&gt;&quot;;
str += &quot;Full name: &quot; + ui.Title + &quot;&lt;br&gt;&quot;;
str += &quot;E-mail: &quot; + ui.EMail + &quot;&lt;br&gt;&quot;;
str += &quot;Department: &quot; + ui.Department + &quot;&lt;br&gt;&quot;;
str += &quot;&lt;img alt='' src='&quot; + picSrc + &quot;' /&gt;&quot;;

document.write(str);
&lt;/script&gt;

And you get a result like this:
IMG

I will follow up this article with an article describing how to hide or show form fields based on membership – or not membership – in a SharePoint group.

Alexander

Headings in list views

Updated 23.09.2009
In this post i will show you how to add heading-support to a SharePoint list view. This is a follow-up on my previous post Headings in SharePoint Forms – jQuery.

It is a precondition for understanding and utilizing this solution that you read the previous article.

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a subsite named “test” with a subsite named “English” with a document library named “Javascript”):
IMG

The scripts used in this solution is “jquery-1.3.2.min.js” and “HeadingsInSharePointListViews.js”. The script “HeadingsInSharePointLists.js” is used to add heading-support to NewForm, DispForm and EditForm as described in the previous article.

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

Here is the sourcecode for the file HeadingsInSharePointListViews.js:

/* Show headings from &quot;Single line of text&quot; in list views
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * LastMod: 23.09.2009
 * ---------------------------------------------
 * This script is an add-on to the script that converts a singleline text field column to a heading.
 * It is a precondition for understanding and utilizing this solution that you  read the previous article:
 * https://spjsblog.com/2009/09/11/headings-in-sharepoint-forms-jquery/

Call like this in your list view (boxed or preview pane):
  &lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
  &lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/HeadingsInSharePointListViews.js&quot;&gt;&lt;/script&gt;
  &lt;script type=&quot;text/javascript&quot;&gt;
    showHeadings(190,true,false,true,'#ebf3ff');
  &lt;/script&gt;

  Parameters explained:
  divWidth: The width of the &quot;label&quot; colomn - where the fieldname is found. 
  paddingTop: Adds a br-tag above the heading to make some air
  paddingBottom: Adds a br-tag below the heading to make some air
  stretch: Adds &quot;colspan:2&quot; to make the heading stretch to the full width of the list form
  bgColor: Background - [optional] background color of the td-tag. You can &quot;copy&quot; the color from
  		   another style to make the background adapt to changing site themes by using this as the
  		   parameter bgColor (no quotes - it's not a string):
  		   $('.ms-quicklaunchheader').css('background-color')
*/

function showHeadingsInView(divWidth,paddingTop,paddingBottom,stretch,bgColor){
// Remove the &quot;heading&quot; from the &quot;viewheader&quot; - not in &quot;bacictable&quot;
if($(&quot;.ms-listviewtable.ms-basictable&quot;).length==0){
	$(&quot;.ms-viewheadertr th&quot;).each(function(){
		var table = $(this).find('table');
		if(table.attr('displayname')!=undefined &amp;&amp; table.attr('displayname').indexOf('#H#')==0){
			table.hide();
		}
	});
}

// Listviews in &quot;basictable&quot; style 
if($(&quot;.ms-listviewtable.ms-basictable&quot;).length&gt;0){
	// Loop trough all columns in the table header to detect headings
	$(&quot;.ms-viewheadertr th&quot;).each(function(colIndex){
		var table = $(this).find('table');
			if(table.attr('displayname')!=undefined){
				var dispname = table.attr('displayname');			
				if(dispname.indexOf('#H#')==0){
					var dispnameClean = dispname.substring(dispname.lastIndexOf('#')+1);		
					$(this).find('table').replaceWith(&quot;&lt;table&gt;&lt;tr&gt;&lt;td class='ms-vb ms-bold' style='white-space:nowrap'&gt;&quot; + dispnameClean + &quot;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&quot;);
			
				var dispNameColor = '#808080'; // Set color to &quot;grey&quot; as standard
				if(dispname.substring(5,12).match(new RegExp(/^#?([a-f]|[A-F]|[0-9]){3}(([a-f]|[A-F]|[0-9]){3})?$/))){ // Find color
					dispNameColor = dispname.substring(5,12);
					$(this).find('td').css({'color':dispNameColor}); // Set color
				}
				// Loop trough all rows and mark the correct cell with the heading's color - toned down to 50% opacity
				// Heading can not be first column from left
				$(&quot;.ms-listviewtable.ms-basictable tr&quot;).each(function(){
					var tdColindex = 1;
					$('td:first', this).nextAll().each(function() {	
						if(tdColindex==colIndex){
							$(this).html(&quot;&lt;div style='background-color:&quot; + dispNameColor + 
							&quot;;width:25px;height:100%;filter:alpha(opacity=50);-moz-opacity:0.5;-khtml-opacity: 0.5;	opacity: 0.5;'&gt;&lt;/div&gt;&quot;)
							.css({'text-align':'center'});;
						}
						tdColindex++;
					});
				});		
			}
		}
	});
}

// Listviews in &quot;Boxed&quot; and &quot;Preview pane&quot; style 

if($(&quot;td.ms-stylelabel&quot;).length&gt;0){ // Boxed style
	findThis = 'td.ms-stylelabel';
}else if($(&quot;#previewpanetable1&quot;).length&gt;0){ // Preview pane style
	findThis = 'td.ms-formlabel';
}

	$(findThis).each(function(){
	if(divWidth!='')$(this).attr('width',divWidth); // Width of label-column&quot;
		if($(this).text().match('#H#')){ // It's a heading
			var customDiv = $(&quot;&lt;div&gt;&lt;/div&gt;&quot;);
			var rawHeading = $.trim($(this).text());
			hSize = rawHeading.substring(3,5); // Find size
			customDiv.css({'fontSize':hSize}); // Set size
			if(rawHeading.substring(5,12).match(new RegExp(/^#?([a-f]|[A-F]|[0-9]){3}(([a-f]|[A-F]|[0-9]){3})?$/))){ // Find color
				customDiv.css({'color':rawHeading.substring(5,12)}); // Set color
			}
			if(stretch){ // Removes the &quot;ms-formbody-td&quot; and sets colspan=2 to stretch the heading
				$(this).next().hide();
				$(this).attr('width','');
				$(this).attr({colSpan:'2'});
			}
			if(typeof(bgColor)!=&quot;undefined&quot; &amp;&amp; bgColor != ''){
				$(this).css({'background-color':bgColor});
			}

			$(this).html(customDiv.text(rawHeading.substring(rawHeading.lastIndexOf('#')+1))); // Set new heading
			if(paddingTop)$(this).prepend('&lt;br&gt;');if(paddingBottom)$(this).append('&lt;br&gt;');	// Padding
		}
	});
}

Copy the code and save as a text-file. Rename it “HeadingsInSharePointListViews.js”, and upload it to your Javascript library as shown above. Be sure to get the code copied right – with no “word wrap” in your texteditor.

You then add a CEWP below the list view webpart in your list and calls the script like this:
IMG

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/HeadingsInSharePointListViews.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
showHeadingsInView(100,true,false,true,$(&quot;.ms-quicklaunchheader&quot;).css('background-color'));
&lt;/script&gt;

The end result should look like this for “Boxed view”:
IMG

Like this for “Preview Pane”:
IMG

And like this for “Basic Table”
IMG

Have fun – and please ask if something is unclear!
Alexander

Headings in SharePoint Forms – jQuery

01.03.2010 Added another method of building headings here: Headings for SharePoint forms – another method

Modified 10.10.2009: Small update for compatibility with the script Narrowing list form to one column.

This script adds heading-support to a custom SharePoint List by using a “prefix” in the field name of a standard “Single line of text” field, and a script to search all column names and reformat it as a heading in NewForm, DispForm and EditForm.

Create the “headings” by adding a column of type “Single line of text” to your list – and prefix your heading with #H# in the column name like this #H#ThisIsMyHeading. You specify the font size in pixes like this #H#17#ThisIsMyHeading, and you can add a custom color to the heading by adding a hex-color code like this #H#17#FF0000#ThisIsMyHeading.

You can also add a background color to your heading as a parameter in your script call like this:

// Specify color like this
showHeadings(190,true,false,true,'#ebf3ff');
// Or inherit like this:
showHeadings(190,true,false,true,$('.ms-formbody').css('background-color'));

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a subsite named “test” with a subsite named “English” with a document library named “Javascript”):
IMG

The script “HeadingsInSharePointLists.js” has this sourcecode:

/* Headings from "Single line of text"
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * LastMod: 10.10.2009
 * ---------------------------------------------
   Example: Create a field of type "Single line of text" like this:
   #H#17#FF0000#This is a RED heading

  	 #H# - Defines heading
 	 17 - Font size
 	 #FF0000# - [optional] color

   If used without specifying the color it looks like this:
 	 #H#17#This is a heading 

Call like this in NewForm.aspx, DispForm.aspx or EditForm.aspx:
  <script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
  <script type="text/javascript" src="/test/English/Javascript/HeadingsInSharePointLists.js"></script>
  <script type="text/javascript">
    showHeadings(190,true,false,true,'#ebf3ff');
  </script>

  Parameters explained:
  divWidth: The width of the "label" colomn - where the fieldname is found. This script removes the nobr-tag from the label
            to prevent long field names to distort the column width.
  paddingTop: Adds a br-tag above the heading to make some air
  paddingBottom: Adds a br-tag below the heading to make some air
  stretch: Adds "colspan:2" to make the heading stretch to the full width of the list form
  bgColor: Background - [optional] background color of the td-tag. You can "copy" the color from
  		   another style to make the background adapt to changing site themes by using this as the
  		   parameter bgColor (no quotes - it's not a string):
  		   $('.ms-formbody').css('background-color')
  		   
  Note: To use with the script "Narrowing list form to one column", you must set the parameter "stretch" to false. 
  You must also call this script before the "Narrowing list form to one column-script".  		   
*/

function showHeadings(divWidth,paddingTop,paddingBottom,stretch,bgColor){
if(divWidth==undefined || divWidth=='')divWidth=190;
	$("td.ms-formlabel").each(function(){
		$(this).children().children('nobr').replaceWith('<div>' + $(this).children().children('nobr').html() + '</div>'); // Removes nobr-tag from label
		$(this).attr('width',divWidth); // Width of all "td.ms-formlabel"
		if($(this).text().match('#H#')){ // It's a heading
			var customDiv = $("<div></div>");
				if($(this).find('div').text()!=''){
					rawHeading = $(this).find('div').text()
				}else{
					rawHeading = $(this).text();
				}
			hSize = rawHeading.substring(3,5); // Find size
			customDiv.css({'fontSize':hSize}); // Set size
			if(rawHeading.substring(5,12).match(new RegExp(/^#?([a-f]|[A-F]|[0-9]){3}(([a-f]|[A-F]|[0-9]){3})?$/))){ // Find color
				customDiv.css({'color':rawHeading.substring(5,12)}); // Set color
			}
			if(stretch){ // Removes the "ms-formbody-td" and sets colspan=2 to stretch the heading
				$(this).next().hide();
				$(this).attr('width','');
				$(this).attr({colSpan:'2'});
			}
			if(typeof(bgColor)!="undefined" && bgColor != ''){
				$(this).css({'background-color':bgColor});
					if(!stretch){
						$(this).next().css({'background-color':bgColor});
					}
			}

			$(this).html(customDiv.text(rawHeading.substring(rawHeading.lastIndexOf('#')+1))); // Set new heading
			if(paddingTop)$(this).prepend('<br>');if(paddingBottom)$(this).append('<br>');	// Padding
			// Hide input
			if(!window.location.href.substring(0,window.location.href.indexOf('?')).match('DispForm.aspx')){
				$(this).next('td').children('span:eq(0)').hide();
				// Preserve borders if stretch = false
				newSpan = $("<span>&nbsp;</span>");
				$(this).next('td').append(newSpan);
			}
		}
	});
}

Save this as a text file and rename to “HeadingsInSharePointLists.js”, then upload to the library as shown above.

Then you add a CEWP below the list form in NewForm, DispForm and EditForm with this sourceCode:

<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/HeadingsInSharePointLists.js"></script>
<script type="text/javascript">
// To inherit the bgColor of the ms-formBody use this
showHeadings(190,true,false,true,$('.ms-formbody').css('background-color'));
// To specify the bgColor do it like this in stead
// showHeadings(190,true,false,true,'#ebf3ff'); 
</script>

Parameters explained:

  • divWidth: The width of the “label” colomn – where the fieldname is found. This script removes the nobr-tag from the label to prevent long field names to distort the column width.
  • paddingTop: Adds a br-tag above the heading
  • paddingBottom: Adds a br-tag below the heading
  • stretch: Adds “colspan:2” to make the heading stretch to the full width of the list form
  • bgColor:  [optional] background color of the td-tag

You can “copy” the color from another style to make the background adapt to changing site themes by specifying the parameter bgColor like this:

showHeadings(190,true,false,true,$('.ms-formbody').css('background-color'));

The end result should look like this:
IMG

Regards
Alexander

A follow-up on this article describing how to add heading-support to a list view is found here.

Dynamic expand/collapse fields or array of fields

28.11.2009 A follow up article on using multi select checkboxes is found here.

13.11.2009 Updated code for better function in EditForm and DispForm

This is a short intro on how to dynamically expand or collapse a field or array of fields based upon selection made in another field.

As always  we begin like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a subsite named “test” with a subsite named “English” with a document library named “Javascript”):
IMG

This is the only script you need to refer for this example.

Create a custom list with these fields:
IMG

Pay attention to the FieldNames. Always create names without spaces or special character to get a nice “FieldInternalName” – then you can rename the field to whatever you want – the “FieldInternalName” newer changes!

The field “MySelect” looks like this:
IMG

Then you add a CEWP below the list form in NewForm like this:
IMG

Sourcecode for NewForm.aspx and EditForm.aspx

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
fields = init_fields();
// Arrays of fields to show or hide
var arrRed = ['ShowIfRed1','ShowIfRed2'];
var arrBlue = ['ShowIfBlue1','ShowIfBlue2'];

// Hide all onload
var arrToHide = [];
arrToHide = arrToHide.concat(arrRed,arrBlue);
toggleArr(arrToHide,true);

// Onchange
$(fields['MySelect']).find('select').change(function(){
var c = $(this).find('option:selected').text();
	dynamicDisplay(c);
});

// Onload
var c = $(fields['MySelect']).find('option:selected').text();
dynamicDisplay(c);

function dynamicDisplay(color){
// Hide all initially
toggleArr(arrToHide,true);
	if(color=='Red'){
		toggleArr(arrRed,false);
	}
	else if(color=='Blue'){
		toggleArr(arrBlue,false);
	}
	else if(color=='Yellow'){
		alert(&quot;No array defined for &quot;Yellow&quot;&quot;);
	}
}

function toggleArr(arr,hide){
  if(hide){
    for(i=0;i&lt;arr.length;i++){
      $(fields[arr[i]]).hide();
    }
  }else if(!hide){
    for(i=0;i&lt;arr.length;i++){
      $(fields[arr[i]]).show();
    }
  }
}

function init_fields(){
var res = {};
$(&quot;td.ms-formbody&quot;).each(function(){
if($(this).html().indexOf('FieldInternalName=&quot;')&lt;0) return;
var start = $(this).html().indexOf('FieldInternalName=&quot;')+19;
var stopp = $(this).html().indexOf('FieldType=&quot;')-7;
var nm = $(this).html().substring(start,stopp);
res[nm] = this.parentNode;
});
return res;
}
&lt;/script&gt;

Sourcecode for DispForm.aspx

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
fields = init_fields();
// Arrays of fields to show or hide
var arrRed = ['ShowIfRed1','ShowIfRed2'];
var arrBlue = ['ShowIfBlue1','ShowIfBlue2'];

// Hide all onload
var arrToHide = [];
arrToHide = arrToHide.concat(arrRed,arrBlue);
toggleArr(arrToHide,true);

// Show the array for the selected color
var c = $(fields['MySelect']).find('.ms-formbody').text().replace(/s|xA0/g,'');
dynamicDisplay(c);

function dynamicDisplay(color){
// Hide all initially
toggleArr(arrToHide,true);
	if(color=='Red'){
		toggleArr(arrRed,false);
	}
	else if(color=='Blue'){
		toggleArr(arrBlue,false);
	}
	else if(color=='Yellow'){
		alert(&quot;No array defined for &quot;Yellow&quot;&quot;);
	}
}

function toggleArr(arr,hide){
  if(hide){
    for(i=0;i&lt;arr.length;i++){
      $(fields[arr[i]]).hide();
    }
  }else if(!hide){
    for(i=0;i&lt;arr.length;i++){
      $(fields[arr[i]]).show();
    }
  }
}

function init_fields(){
var res = {};
$(&quot;td.ms-formbody&quot;).each(function(){
if($(this).html().indexOf('FieldInternalName=&quot;')&lt;0) return;
var start = $(this).html().indexOf('FieldInternalName=&quot;')+19;
var stopp = $(this).html().indexOf('FieldType=&quot;')-7;
var nm = $(this).html().substring(start,stopp);
res[nm] = this.parentNode;
});
return res;
}
&lt;/script&gt;

The function “init_fields” is a modified version of the one created by Erucy and posted here. I have modified it to use “FieldInternalName” instead of “DisplayName” for locating the fields.

The end result should look like this:
IMG
IMG
IMG
IMG

Other fields can be used to trigger the event – look here to learn how to refer the various types of fields:
http://docs.jquery.com/Selectors

Have fun!
Alexander