All posts by Alexander Bautz

Reformat URL from calculated column with decent clickable link text

19.11.2009 Fixed the bugs from yesterday…
18.11.2009 Added support for grouped views.
17.11.2009 Updated code to remove the filter in the viewheader for the field with the calculated column “Link”. Noted by Paulo Sousa in the post comments. I have also added a code to use for the DispForm to format the link.

If you create a link from a calculated column it is not formatted right in the list view. Here it a small jQuery script to fix this issue.

The list has these columns:
IMG

The code in the calculated column “Link” looks like this:

="<a title='The mouseover text goes here' href='"&URL&"'>"&Description&"</a>"

The NewForm looks like this:
IMG

The list view looks like this before the script is added:
IMG

And like this after the script is added:
IMG

How is it done?

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.

Add a CEWP below the list view 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">
// Updated 19.11.2009
// Reformat the url in the calculated column
reformatCalculatedColumnUrl();

function reformatCalculatedColumnUrl(){
	$(".ms-listviewtable td.ms-vb2:contains('href')").each(function(){
		$(this).html($(this).text())
	});
}

// If grouped by the calculated column - reformat the group header
var groupHeaderClickableLink = true; // Set to false to remove the link on the groupheader
$(".ms-listviewtable td[class^='ms-gb']").each(function(){
	if($(this).html().indexOf('<')>0){
		// Get full HTML of group header
		var rawHTML = $(this).html();
		// Extract the part before the calculated column's content
		var preWrap = rawHTML.substring(0,rawHTML.indexOf("<"));
		// Extract the part after the calculated column's content
		var postWrap = rawHTML.substring(rawHTML.lastIndexOf('>')+4);
	
		if(!groupHeaderClickableLink){
			// Find the clickable part of the calculated column's content 
			var linkTextStart = rawHTML.indexOf('>') + 4;
			var linkTextStop = rawHTML.lastIndexOf('<');
			var linkText = rawHTML.substring(linkTextStart,linkTextStop);
			// Write back the HTML
			$(this).html(preWrap + linkText + postWrap);
		}else{
			// Find the clickable part of the calculated column's content 
			var linkStart = rawHTML.indexOf('<');
			var linkStop = rawHTML.lastIndexOf('>') + 4;
			// Find raw link				
			var rawLink = rawHTML.substring(linkStart,linkStop);
			// Find the parts to keep
			var pre = rawLink.substring(0,rawLink.indexOf('href=') + 6);
			var mid = rawLink.substring(rawLink.lastIndexOf('href=')+6,rawLink.indexOf('>')-1);
			var post = rawLink.substring(rawLink.indexOf('>')-1);
			// Get the full url and replace the < and >
			var fullUrl = (pre + mid + post).replace(/</g,'<').replace(/>/g,'>');
			// Write back the HTML
			$(this).html(preWrap + fullUrl + postWrap);	
		}
	}
});

// Disable the filter for the field named "Link"
$(".ms-viewheadertr table[displayname='Link']").parents('th:first').removeClass('ms-vh2').addClass('ms-vh2-nograd').html("Link");

// Attaches a call to the function "reformatCalculatedColumnUrl" 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);
reformatCalculatedColumnUrl();
}
</script>

The “group by calculated column – feature” is requested by Paulo Sousa. You can specify whether to have a clickable link or a plain text in the group header by setting the parameter “groupHeaderClickableLink” in line 14 to true or false.

In line 50 in this codeblock i have disabled the filter link in the view header for the field with DisplayName “Link” – you must insert your own field’s DisplayName here.

I do not know of a way to “reformat” the filter values for a calculated column, and therefore the filter values will be like the calculated column’s raw value – therefore i found that disabling the filter was he best option.

For use with DispForm – add code to CEWP below list form (edit the location of jquery as needed):

<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
fields = init_fields();

// This is an array of FieldInternalNames for all fields to reformat
var arrOfFieldsToReformat = ['Link'];
// Loop trough all fields in array and reformat to decent link
$.each(arrOfFieldsToReformat,function(idx,item){
	var LinkField = $(fields[item]).find('.ms-formbody');
	LinkField.html(LinkField.text());
});

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

In line 6 you must insert your own FieldInternalNames!
You can add multiple FieldInternalNames, comma separated.

This will result in a DispForm like this:
IMG

That’s it!

Regards
Alexander

Hide empty rows in DispForm

17.06.2010 Updated code to support this heading script: Headings in SharePoint Forms – jQuery

Have you ever wanted to hide rows from your DispForm if they are empty?

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.

To begin with NewForm looks like this:
IMG

And your DispForm like this:
IMG

Add a CEWP below your DispForm list-form like this:
IMG

With this code:

$("td.ms-formbody").each(function(){
// Trim off all white spaces
var val = $(this).text().replace(/\s|xA0/g,'');
// Check the string length - if it's 0 hide the field
	// If it is not a heading - hide it
	if($(this).parents().html().match('FieldName="#H#')==null){
		if(val.length==0){
			$(this).parents('tr:first').hide();
		}
	}
});

Now your DispForm should look like this:
IMG

If you want to hide the table row based on other criteria, adapt the check in line 7 to fit your need – either it’s by length or it’s by string comparison.

Regards
Alexander

Accumulate selections from dropdown to mulitline textfield

19.02.2010: Updated the post with a code example for setting this drop downs as required (requested by Larry). You find the code at the bottom of the article.

28.12.2009: Updated to add support for an optional delimiter character. I have also updated the functionality so that the selection now is moved from the drop down to the “Accumulated selection” when selected (and back when removed from the selection).


This article demonstrates a solution for accumulating single choice selections from a drop down to a hidden multi-line text-field.

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 sourcecode for the file “AccumulateSelectionsToMultilineText.js” is found below.

Add the columns to your list like the picture below. I have two Choice columns with some random choices and a multi-line plain text column to accumulate the selections in.

NOTE:
The default selection must be blank to be able to select the “first value” and accumulate it as the function triggers on change event on the Choice columns.

Add a CEWP below your NewForm list-form (and EditForm if you like) 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/AccumulateSelectionsToMultilineText.js"></script>
<script type="text/javascript">
// Array of fields to accumulate values from - format: FieldInternalName of select [delimiter] FieldInternalName of hidden accumulation field
arrFieldsToAccumulate = ['Choice|ChoiceAccumulated','Choice2|Choice2Accumulated'];
// Call function to iterate trough all constellations of fields and hidden accumulator-fields in array defined above
// The function "addAccumulateFunctionOnLoad" now takes one argument: sepChar. This is the  optional delimiter character to separate the selected values
addAccumulateFunctionOnLoad(';');
</script>

Your NewForm should now look like this:
IMG
Note: The 28.12.2009-update now moves the selection from the dropdown and to the “Accumulated selections”.

And your list view should look like this:
IMG

Some info:

  • The selected values are stored in custom div’s and added to the hidden multi-line text-field when clicking the “OK” button
  • The delimiter in the multi-line text-field is “n”, and the choices therefore is rendered in separate lines in the list-view and in DispForm.aspx
  • The code handles page reload due to form validation and “preserves” the values (it reads them back from the hidden field and rewrites the custom div’s)
  • Note that the actual selection-dropdown is cleared upon form submission
  • When in NewForm or EditForm, a click on one of the accumulated selected values removes the selected element

Sourcecode for the file “AccumulateSelectionsToMultilineText.js”:

/* Accumulate selections from dropdown to multi-line text-column
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * LastMod: 26.12.2009
 * ---------------------------------------------
 * Must include reference to jQuery
 * ---------------------------------------------
 *
 * Call from CEWP BELOW the list form in NewForm.aspx or EditForm.aspx with the code provided in the blog post.
*/
fields = init_fields();

function addAccumulateFunctionOnLoad(sepChar){
if(typeof(sepChar)=='undefined'){
	separatorChar = '';
}else{
	separatorChar = sepChar;
}

	$.each(arrFieldsToAccumulate, function(idx,item){
		var split = item.split('|');
		var from = split[0];
		var to = split[1];
		accumulateOnSelect(from,to);
	});
}

function accumulateOnSelect(fieldFrom,fieldTo){
	// Hide the "hidden" accumulation-input
	$(fields[fieldTo]).hide();
	// Add onchange on select
	$(fields[fieldFrom]).find('select').change(function(){
		if($(this).find('option:selected').val()!=''){		
			appendValuesFromSelect(fieldFrom);
		}
	});
	
	var selectFromTd = $(fields[fieldFrom]).find('.ms-formbody');
		selectFromTd.append("<span>Accumulated selections:</span>");
	var currentSelections = $(fields[fieldTo]).find(':input').val().split("n");
	$.each(currentSelections,function(idx,itemValue){
		if(itemValue!=''){
			var newSel = customAddSelection(fieldFrom,itemValue);			
			// Append new selection
			selectFromTd.append(newSel);
		}	
		// Remove the selected value from the drop down					  	
		selectFromTd.find('select option').each(function(){
			if(itemValue!=''){
				var iVal = handleSeparatorChar(itemValue,true);
				if($(this).val()==iVal){
					$(this).remove();
				}
			}
		});
	});	
addCustomClassAttr(fieldFrom);
}

function appendValuesFromSelect(fieldFrom){
	var selectFromTd = $(fields[fieldFrom]).find('.ms-formbody');
	var selectedOpt = selectFromTd.find('select option:selected');
	var selectedvalue = selectedOpt.val();
	if(selectedvalue!=''){
		var newSel = customAddSelection(fieldFrom,selectedvalue);
		// Append new selection
		selectFromTd.append(newSel);	
		// Remove the selected value from the drop down		  	
		selectedOpt.remove();
		// Add hover effect
		addCustomClassAttr(fieldFrom);
	}
}

function customAddSelection(fField,sValue){
sValue = handleSeparatorChar(sValue,false);
var newDiv = $("<div title='Click to remove from selection' " +
				"class='dummyClass_" + fField + "' " +
				"style='cursor:pointer;padding-left:4px'>" + sValue + "</div>");
// Add one time onclick even
newDiv.one("click",function(){removeSelectedDiv($(this))});
return newDiv;
}

function addCustomClassAttr(fieldFrom){
	$(".dummyClass_" + fieldFrom).hover(function(){
		$(this).addClass("ms-alternating");						
	},
	function(){
		$(this).removeClass("ms-alternating");	
	});	
}

function removeSelectedDiv(obj){
var objVal = obj.text();
var objVal = handleSeparatorChar(objVal,true);
obj.parents('td:first').find('select').append("<option value='" + objVal + "'>" + objVal + "</option>");
	obj.css({'background-color':'red'});
	obj.fadeTo(450,0,function(){obj.remove();});	
}

function handleSeparatorChar(str,remove){
var newStr = '';
	if(separatorChar==''){
		newStr = str;	
	}else{
		if(remove){
			newStr = str.substring(0,str.indexOf(separatorChar));
		}else{
			if(str.indexOf(separatorChar)>-1){
				newStr = str;
			}else{
				newStr = str + separatorChar;
			}			
		}
	}
	return newStr;
}

function PreSaveAction(){
	$.each(arrFieldsToAccumulate, function(idx,item){
		var from = item.split('|')[0];
		var to = item.split('|')[1];
		// Find all selected values
		var str = '';
		$(".dummyClass_" + from).each(function(){
			if($(this).text()!=''){
				str += $(this).text() + "n";
			}
		});
		// Insert the values in the hidden field	
		$(fields[to]).find(':input').val(str);
	});
return true; 
}

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

Save the file as “AccumulateSelectionsToMultilineText.js” and upload to the document library or folder as described above.

How to set the fields as required:
Open the file “AccumulateSelectionsToMultilineText.js” and remove the function PreSaveAction(). Then you add this modified PreSaveAction(), and the lines for setting the red “required field” star after the label to the CEWP code.

You can NOT set the field as requires under list settings.

// Red star
$(fields['Choice']).find('.ms-formlabel h3').append("<span class='ms-formvalidation'> *</span>");
$(fields['Choice2']).find('.ms-formlabel h3').append("<span class='ms-formvalidation'> *</span>");

function PreSaveAction(){
var okToSave = true;
	$.each(arrFieldsToAccumulate, function(idx,item){
		var from = item.split('|')[0];
		var to = item.split('|')[1];
		// Find all selected values
		var str = '';
		$(".dummyClass_" + from).each(function(){
			if($(this).text()!=''){
				str += $(this).text() + "n";
			}
		});
		// Insert the values in the hidden field
		$(fields[to]).find(':input').val(str);
		// Remove validation message to avoid multiple messages
		$(fields[from]).find('.ms-formbody div.ms-formvalidation').remove();
		// Check if the "to-field" is empty, and that the field is set to required
		if(str=='' && $(fields[from]).find('.ms-formlabel span.ms-formvalidation').length==1){			
			// Add validation message
			$(fields[from]).find('.ms-formbody').append("<div class='ms-formvalidation'>You must specify a value for this required field.</div>");
			// Set "save item" to false
			okToSave = false;		
		}

	});
// Returns true or false
return okToSave; 
}

Please ask if something is not clear to you.

Alexander

Narrowing list form to one column

Updated 10.10.2009: To use this script with the script Headings in SharePoint Forms – jQuery you must set the parameter “stretch” for the heading script to false. You must also call the heading script before the “Narrowing list form to one column-script”.

I have made a tiny update to the heading script to support setting the backgound color when using it with “Narrowing list form to one column-script”.


I got a request for a solution to narrow down the list form to one column with the “formlabel” above the “formbody”.
This is actually a very simple task.

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.

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

Add this code to the CEWP:

<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
$(document).ready (function() {	
	// This image sets the width of the form to min 590px - it must be removed
	$('#onetIDListForm img[width=590]').remove();	
});

$("td.ms-formlabel").each(function(){
	// Get the html of the formlabel
	var label = $(this).html();	
	// Insert the label over the formbody
	$(this).parents('tr:first').find('.ms-formbody').prepend(label);
	// Remove the original label
	$(this).remove();
	
});
</script>

Note: If you use this solution with other solutions that modifies the formbody – like the Wrap choice-field in multiple columns, you have to call this script last to have the “formlabel” added in the right position.

Your end result should look like this:
IMG

As always – ask if you do not understand how to use this script

Alexander

Get variables in a script from another list

This one is a follow-up on the two previous posts on manipulating form labels, but the technique can be used for many other scenarios.

I will show you how to use entries from another list as “resources” for a script by querying the “resources-list” for variables to use in the script.

Because i use the same scenario as in the two previous posts, you must read them before proceeding with this one.

The array i used to populate the labels for each option in the choice list is now moved out in another list, and we “ask for it” with a CAML-query to the web-service lists.asmx.

Create a new list like this:
IMG
These are all the actual “FieldInternalNames” (Fields get their “FieldInternalName” first time created – any later modification of the name affects only their “DisplayName”).

The NewForm looks like this:
IMG

The fields are used like this:

  • ListGuid: This is the list Guid of the list using this resource.
  • Title: This is the FieldInternalName of the column to use the resource in.
  • Val: This is the actual value to return.
  • Description: This is used to describe the use of this “resource”.

Here is the form filled for our need in this example:
IMG

You must refer two more scripts like this:
IMG

The scripts “interaction.js” and “stringBuffer.js” is created by Erucy and published on CodePlex.

You will need the list GUID of your “resources-list” and the list you will insert the resources in. Replace the one in the script (resourcesListGuid – line 34 in the script) and the one in the picture above and the one in line 13 with your own!

I have made the “resources-list” more complex than i needed for this example, but by adding the “ListGuid” column i prepare the list so that it can be used by many different lists without having to think about using unique names for the columns in the different lists.

The script is now modified like this:

<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">
fields = init_fields();

var myNewLabel = "<br><div>Here is some custom text added by adressing the formlabel with jQuery!</div>" +
				 "<br><div>You can insert images to:<br><img src='/test/English/Shared%20Documents/jQuery_img.jpg' border='0'></div>"

$(fields['MyChoice']).find(".ms-formlabel h3").after(myNewLabel);

// Lookup the array values from another list and split it to create an array
arrMyChoice = getExternalResources('3264E665-228B-4A08-970C-4CE5E871A002','MyChoice').split(',');

// Call the script that inserts the descriptions
descriptionBeforeChoice('MyChoice',arrMyChoice,300);

function descriptionBeforeChoice(FieldInternalName,arrName,widthOfCustomLabel){
	$(fields[FieldInternalName]).find(".ms-formbody").find(":checkbox").each(function(idx){
		// Add alternating style to make it easier to follow the lines in the form
		var trClass = '';
		if(idx%2==0){
			trClass = 'ms-alternatingstrong';
		}
		$(this).before("<span style='display:inline-block;width:" + widthOfCustomLabel + ";white-space:nowrap'>" + arrName[idx] + "</span>")
			.parent().css({'white-space':'nowrap'})
			.parents('tr:first').addClass(trClass);
	});
}

function getExternalResources(ListGuid,ElementTitle){ 
// ListGuid can be swapped with DisplayName
// ElementTitle is here the FieldInternalName of the field to add the resources to
var resourcesListGuid = "{7008EA8E-623D-4F80-A70C-35C0F71E5FB0}"; // GUID of the "resources-list" - can be swapped with DisplayName 
var query = "<Where><And><Eq><FieldRef Name='ListGuid' /><Value Type='Text'>" + ListGuid + "</Value></Eq>" +
			"<Eq><FieldRef Name='Title' /><Value Type='Text'>" + ElementTitle + "</Value></Eq></And></Where>";
wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/';
var res = queryItems(resourcesListGuid,query,['Val'],1);
	if(res.count==-1){
		alert("An error occured in the query:n" + query);
	}else{
		return res.items[0]['Val'];
	}
}

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

The end result looks like in the previous post:
IMG

As always – ask if something is unclear!
Alexander

Add individual label for each choice in multichoice list

This post describes how to add individual labels for each choice in a multi choice list.
This is a follow-up on the post on how to modify formlabel in SharePoint form.

The end result looks like this:
IMG

Read the previous post to get the basics, then modify the code like this:

<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
fields = init_fields();

var myNewLabel = "<br><div>Here is some custom text added by adressing the formlabel with jQuery!</div>" +
				 "<br><div>You can insert images to:<br><img src='/test/English/Shared%20Documents/jQuery_img.jpg' border='0'></div>"

$(fields['MyChoice']).find(".ms-formlabel h3").after(myNewLabel);

// Array of all descriptions - must be the same number of elements as the number of choices in the choice-list
var arrMyChoice = ['Description for choice 1',
				'Description for choice 2',
				'Description for choice 3',
				'Description for choice 4',
				'Description for choice 5',
				'Description for choice 6',
				'Description for choice 7',
				'Description for choice 8',
				'This is the longest: Description for choice 9',
				'Description for choice 10',
				'Description for choice 11',
				'Description for choice 12',
				'Description for choice 13',
				'Description for choice 14',
				'Description for choice 15',
				'Description for choice 16',
				'Description for choice 17',
				'Description for choice 18',
				'Description for choice 19',
				'Description for choice 20'];

// Call the script that inserts the descriptions
descriptionBeforeChoice('MyChoice',arrMyChoice,300);

function descriptionBeforeChoice(FieldInternalName,arrName,widthOfCustomLabel){
	$(fields[FieldInternalName]).find(".ms-formbody").find(":checkbox").each(function(idx){
		// Add alternating style to make it easier to follow the lines in the form
		var trClass = '';
		if(idx%2==0){
			trClass = 'ms-alternatingstrong';
		}
		$(this).before("<span style='display:inline-block;width:" + widthOfCustomLabel + ";white-space:nowrap'>" + arrName[idx] + "</span>")
			.parent().css({'white-space':'nowrap'})
			.parents('tr:first').addClass(trClass);
	});
}

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

Follow up: Get variables in a script from another list

Feel free to ask if anything is unclear!
Alexander

Modify formlabel in SharePoint form

I got a request for a method to edit the form label of a SharePoint form. Basically this i very easy.

This is how it looks by default:
IMG

By adding a few lines of code it can look like this:
IMG

How is it done?


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.

Add a CEWP below your NewForm list-form (and EditForm if you like) 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">
fields = init_fields();

var myNewLabel = "<br><div>Here is some custom text added by adressing the formlabel with jQuery!</div>" +
				 "<br><div>You can insert images to:<br><img src='/test/English/Shared%20Documents/jQuery_img.jpg' border='0'></div>"

$(fields['MyChoice']).find(".ms-formlabel h3").after(myNewLabel);

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

The function init_fields() is a modified version of Erucy’s function for finding fields in a SharePoint form. My modified version uses FieldInternalName rather than DisplayName to locate the fields.

Thats it!
Alexander

If you want to add individual labels for each of the choices in the choice list- read this post to learn how.

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