Monthly Archive for June, 2010

Vertical Scrolling WebPart

15.07.2010 Updated the code for the file “VerticalScrollingWebPart.js” to try to fix the overflow issue in IE6.

24.06.2010 The code for the file “VerticalScrollingWebPart.js” is updated. I actually forgot to update it as i made some adjustments in the CEWP code… I hope the overflow issue is fixed with this update.

I got this request from Charlie:

…a solution that I can use as a dashboard web part to vertically scroll the most recent “top 10” items from a 2nd list?

Example: I need to be able to grab the most recently items marked as “Completed” or “Sold” from a list and be able to show 2-3 fields in the scroller.

Click here for a crude example – the scrolling is not so choppy – this is only a animated gif to give you a hint of how the solution works.

The scrolling action pauses when hovered with the mouse.

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.4.2.min. Update the script “src” if you use another version.

The sourcecode for the file “VerticalScrollingWebPart.js” is provided below.

Add this code to a CEWP where you want the scrollable contents to appear:

<script type="text/javascript" src="/test/English/Javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/VerticalScrollingWebPart.js"></script>
<script type="text/javascript">
init_fillScrollableDiv({'listGuid':'A4B4E15A-C5B0-47BC-A08B-739CD48FE58A',
	'listBaseUrl':'/test/English',
	'listViewGuid':'5BD378F4-25D5-4880-9C5B-1667FE43978D',											
	'viewFields':['Title','MultiLine'],
	'viewFieldsStyle':['font-weight:bold','padding:0 0 0 5'],	
	'divID':'myScrollableDiv',
	'divHeight':'250px',
	'divWidth':'500px',	
	'speed':4,
	'linkBack':true});
</script>

Read here how to find the FieldInternalNames for your fields and the “listGuid” or “listViewGuid” for your list.

Parameters explained:

  • listGuid: GUID of the source-list
  • listBaseUrl: Relative URL to site/subsite. If root site use only two quotes representing a blank string – like this: “”.
  • listViewGuid: GUID of the view to use from the source-list – if left empty it uses the default view.
  • viewFields: Array of FieldInternalNames to use in the scrolling webpart.
  • viewFieldsStyle: Array of styles to match the array of FieldInternalNames above.
  • divID: The ID of the scrollable DIV. Can be anything as long as it is unique on the page.
  • divHeight: The hight of the scrollable area.
  • divWidth: The width of the scrollable area.
  • speed: A number representing the scroll speed.
  • linkBack: If set to true it adds a link to open the item clicked on.

The sourcecode for the file “VerticalScrollingWebPart.js”:

/* Pulls items from a list view and presents them as a Vertical Scrolling Web Part
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * http://spjsblog.com
 * Copyright (c) 2009-2010 Alexander Bautz (Licensed under the MIT X11 License)
 * v1.2
 * LastMod: 14.07.2010
 	- LastChange: Attempted to fix the overflow issue in IE6
 * ---------------------------------------------
 * Include reference to:
 *  jquery - http://jquery.com
 *	VerticalScrollingWebPart.js (this file)
 * ---------------------------------------------
*/

function init_fillScrollableDiv(obj){
	// Build the div
	var myDivBuffer = [];
	myDivBuffer.push("<div style='vertical-align:top;position:relative;overflow:hidden;cursor:default;height:"+obj.divHeight+";width:"+obj.divWidth+"'>");
	myDivBuffer.push("<div id='"+obj.divID+"' style='position:relative'></div>");
	myDivBuffer.push("</div>");
	myDivContainer=myDivBuffer.join('');	
	document.write(myDivContainer);
	$(document).ready(function(){
		fillScrollableDiv(obj)
	});
}

function fillScrollableDiv(info){
	wsBaseUrl = info.listBaseUrl + '/_vti_bin/';
	info.animBegin = 0;
	info.animPart = 0;
	// Query the list for items	
	var res = queryItemsByViewName(info.listGuid,info.listViewGuid,info.viewFields.concat('ID','FileDirRef'));
	if(res.count==-1)alert("An error occured - check the parameters "listGuid", "listBaseUrl" and "listViewGuid".");
	var finalBuffer = [];
	var path = '';
	$.each(res.items,function(i,item){
		var partBuffer = [];
		if(path==''){
			var pathRaw = item['FileDirRef'];
			path = "/"+pathRaw.substring(pathRaw.indexOf(';#')+2);
		}	
		$.each(info.viewFields,function(idx,fin){
			var style = '';
			var thisVal = (item[fin]==null)?'':item[fin];
			if(thisVal.indexOf(';#')>-1){
				thisVal = thisVal.substring(thisVal.indexOf(';#')+2);
			}			
			if(info.viewFieldsStyle[idx]!=undefined){
				style = " style='"+info.viewFieldsStyle[idx]+"'";
			}
			partBuffer.push("<tr><td"+style+">"+thisVal+"</td></tr>");	
		});		
		finalBuffer.push("<hr style='height:1px;color:black' />");
		if(info.linkBack){
			finalBuffer.push("<table title='Go to item' style='cursor:pointer' ");
			finalBuffer.push("onclick='javascript:location.href=""+path+"/DispForm.aspx?ID="+item['ID']+"&Source="+location.href+""' ");
			finalBuffer.push("cellspacing='0' cellpadding='0'>"+partBuffer.join('')+"</table>");
		}else{
			finalBuffer.push("<table cellspacing='0' cellpadding='0'>"+partBuffer.join('')+"</table>");
		}	
	});
	var myContents = finalBuffer.join('');
	// Update the content in the scrollable div
	$("#"+info.divID).html(myContents)
		.mouseenter(function(){			
			var now = new Date();
			info.animPart += (now-info.animBegin);		
			$(this).stop();
		})
		.mouseleave(function(){		
			$(this).stop();
			var partScr = parseInt($(this).css('top'));
			scrollMyDiv(partScr,info);
		});
	// Call scroll function
	scrollMyDiv('',info);
}

function scrollMyDiv(scroll,info){
	info.animBegin = new Date();
	var myDiv = $("#"+info.divID);
	var ch = myDiv.height();
	var chpHeight = myDiv.parent().height();	
	if(scroll==''){
		var scroll=chpHeight;
	}
	var duration = (ch*(info.speed*10))-info.animPart;
	myDiv.css({'top':scroll}).animate({"top":-ch},duration,'linear',function(){
		info.animPart = 0;
		scrollMyDiv('',info);
	});
}

// Function to pull items from view
function queryItemsByViewName(listName, viewName, viewFields, pagingInfo){
	var content = buildQueryContentByViewName(listName, viewName, viewFields, pagingInfo);
	var result = {count:-1, nextPagingInfo:'', items:[]};
	wrapSoapRequest(wsBaseUrl + 'lists.asmx', 'http://schemas.microsoft.com/sharepoint/soap/GetListItems', content, function(data){
		result.count = $('rs\:data', data).attr('ItemCount');
		result.nextPagingInfo = $('rs\:data', data).attr('ListItemCollectionPositionNext');
		$('z\:row', data).each(function(idx, itemData){
			var fieldValObj = {}
			$.each(viewFields,function(i,field){
				var value = $(itemData).attr('ows_' + field);
				if(value == undefined) value = null;
				fieldValObj[field]=value;
			});	
			result.items.push(fieldValObj);		
		});
	});
	return result;
}

function buildQueryContentByViewName(listName, viewName, viewFields, pagingInfo){
	var result = [];
	result.push('<GetListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/">');
	result.push('<listName>' + listName + '</listName>');
	result.push('<viewName>' + viewName + '</viewName>');
	if(viewFields != null && viewFields.length > 0){
		result.push('<viewFields><ViewFields xmlns="">');
		$.each(viewFields, function(idx, field){
			result.push('<FieldRef Name="' + field + '"/>');
		});
		result.push('</ViewFields></viewFields>');
	}
	result.push('<queryOptions><QueryOptions xmlns=""><IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>');
	if(pagingInfo != undefined && pagingInfo != null && pagingInfo != '')
		result.push('<Paging ListItemCollectionPositionNext="' + pagingInfo.replace(/&/g, '&amp;') + '" />');
	result.push('</QueryOptions></queryOptions>');
	result.push('</GetListItems>');
	return result.join('');
}

function wrapSoapRequest(webserviceUrl,requestHeader,soapBody,successFunc){
	var xmlWrap = [];
		xmlWrap.push("<?xml version='1.0' encoding='utf-8'?>");
		xmlWrap.push("<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>");
		xmlWrap.push("<soap:Body>");
		xmlWrap.push(soapBody);
		xmlWrap.push("</soap:Body>");
		xmlWrap.push("</soap:Envelope>");
		xmlWrap = xmlWrap.join('');
	$.ajax({
		async:false,
		type:"POST",
		url:webserviceUrl,
		contentType:"text/xml; charset=utf-8",
		processData:false,
		data:xmlWrap,
		dataType:"xml",
		beforeSend:function(xhr){
			xhr.setRequestHeader('SOAPAction',requestHeader);
		},
		success:successFunc
	});
}

Click the “view source” button, highlight the code in the new window and copy it from there to notepad. Save as “VerticalScrollingWebPart.js” – mind the file extension – and upload to the scriplibrary as shown above.

Ask if anything is unclear!
Alexander

Numbers only in single line text field

18.06.2010 – small update to set “lengthOfInputNumber” relative to the array of FieldInternalnames.

By request from Larry, here is a solution that restricts input in a single line text field to number only.

Note: It is treated as text in SharePoint and you cannot sum or average in a list view.

Add this code to a CEWP below the list form in your NewForm or EditForm:

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

// Allow numbers only in text fields
// Array of FieldInternalNames to lilmit to numbers only
var arrToCheckForNum = ['Title','Num'];
// Array of length of input string - set to 255 if you do not want to limit input length. Corresponds to the same array index in the "arrToCheckForNum"
var lengthOfInputNumber = [9,2];

$.each(arrToCheckForNum,function(idx,item){
$(fields[item]).find('input').css({'width':'75px'});
	$(fields[item]).find('input').keyup(function(e){
		var thisVal = $(this).val();
		thisVal = thisVal.substring(0,lengthOfInputNumber[idx]);
		$(this).val(thisVal.replace(/[^0-9]/g,''));
	}).blur(function(){
		var thisVal = $(this).val();
		thisVal = thisVal.substring(0,lengthOfInputNumber[idx]);
		$(this).val(thisVal.replace(/[^0-9]/g,''));
	});
});

/*
  LastMod: 07.05.2010
*/
function init_fields_v2(){
	var res = {};
	$("td.ms-formbody").each(function(){
	var myMatch = $(this).html().match(/FieldName="(.+)"s+FieldInternalName="(.+)"s+FieldType="(.+)"s+/);	
		if(myMatch!=null){
			// Display name
			var disp = myMatch[1];
			// FieldInternalName
			var fin = myMatch[2];
			// FieldType
			var type = myMatch[3];
			if(type=='SPFieldNote'){
				if($(this).find('script').length>0){
					type=type+"_HTML";
				}
			}
			if(type=='SPFieldLookup'){
				if($(this).find('input').length>0){
					type=type+"_Input";
				}
			}
			// Build object
			res[fin] = this.parentNode;
			res[fin].FieldDispName = disp;
			res[fin].FieldType = type;
		}		
	});
	return res;
}
</script>

The parameters “arrToCheckForNum” and “lengthOfInputNumber” must be adapted to suite your needs.

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.4.2.min. If you use another version, remember to update the script “src”.

Alexander

Show field description in list view column header

15.05.2012 You find an updated version here.
08.07.2010 Updated the code to make it cross compatible for SP2007 and SP2010.


I got this request from Saran to display the field description when hovering the mouse over the column header in a list view.

IMG

Add this code to a CEWP and place it below the list view:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">

if(typeof(_fV4UI)==='undefined'){ // SP2007
	var myTooltipObj = customGetList(ctx.listName);	
	$(".ms-listviewtable th").each(function(){
		var fieldDisplayName = $(this).find("table:first").attr('DisplayName');	
		if(fieldDisplayName==undefined){
			fieldDisplayName = $(this).text();
		}
		if(myTooltipObj[fieldDisplayName]!=undefined){
			$(this).attr('title',myTooltipObj[fieldDisplayName]).find('a').attr('title',myTooltipObj[fieldDisplayName]);;
		}
	});
}else{ // SP2010
	var myTooltipObj = customGetList(_spPageContextInfo.pageListId);	
	$("div.ms-vh-div").each(function(){
		var fieldDisplayName = $(this).attr('DisplayName');
		if(myTooltipObj[fieldDisplayName]!==undefined){
			$(this).attr('title',myTooltipObj[fieldDisplayName]);
		}
	});
}

function customGetList(listName){
	xmlStr = [];
	xmlStr.push('<GetList xmlns="http://schemas.microsoft.com/sharepoint/soap/">');
	xmlStr.push('<listName>' + listName + '</listName>');
	xmlStr.push('</GetList>');
	xmlStr = xmlStr.join('');
	var result = {};
	wrapSoapRequest(L_Menu_BaseUrl + '/_vti_bin/lists.asmx', 'http://schemas.microsoft.com/sharepoint/soap/GetList', xmlStr, function(data){
		$('Field', data).each(function(i){
			if(result[$(this).attr('DisplayName')]==undefined || result[$(this).attr('DisplayName')]==''){
				result[$(this).attr('DisplayName')] = ($(this).attr('Description')==undefined)?'':$(this).attr('Description');
			}
		});
	});
	return result;
}

function wrapSoapRequest(webserviceUrl,requestHeader,soapBody,successFunc){
	var xmlWrap = [];
		xmlWrap.push("<?xml version='1.0' encoding='utf-8'?>");
		xmlWrap.push("<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>");
		xmlWrap.push("<soap:Body>");
		xmlWrap.push(soapBody);
		xmlWrap.push("</soap:Body>");
		xmlWrap.push("</soap:Envelope>");
		xmlWrap = xmlWrap.join('');
	$.ajax({
		async:false,
		type:"POST",
		url:webserviceUrl,
		contentType:"text/xml; charset=utf-8",
		processData:false,
		data:xmlWrap,
		dataType:"xml",
		beforeSend:function(xhr){
			xhr.setRequestHeader('SOAPAction',requestHeader);
		},
		success:successFunc
	});
}
</script>

Note: This will only work when only one list view webpart is in the page.

Ask if anything is unclear.

Alexander

Send email with javascript – with the help of a workflow in a dedicated “send email-list”

This solution “injects” data, using JavaScript, into a custom list. This list has one purpose only: sending an email containing the injected data.

The custom list have the following fields:

  • Title: The native Title field – holds the subject.
  • To: Single line of text.
  • Cc: Single line of text.
  • EmailBody: Multiple lines of plain text.

IMG

With a workflow like this:
IMG
IMG
IMG
IMG
IMG
You could skip the part with “Store …. in Variable” for “To” and “Cc” if you like, this step was originally used for pulling values from a multi select people picker. This field cannot be used in “To” or “Cc” without this procedure.

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.4.2.min. If you use another version, remember to update the script “src”.

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

Read here how to add a CEWP to the NewForm or EditForm, how to find the list Guid of your list, and how to find the FieldInternalName of your columns.

Add this code to a CEWP – update the script “src”:

<input type="button" onclick="javascript:init_sendMailWithJavascript()" value="Send test-mail">
<script type="text/javascript" src="../../Javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="../../Javascript/interaction.js"></script>
<script type="text/javascript" src="../../Javascript/stringBuffer.js"></script>
<script type="text/javascript">

var sendMailListGuid = '{68CC0B80-7013-41B2-B591-CBA3899B713D}';

function init_sendMailWithJavascript(){
	sendMailWithJavascript("My custom subject","to@mail.com","cc@mail.com","<div>This is a div</div><a href='http://spjsblog.com'>SharePoint JavaScripts</a>");
}

function sendMailWithJavascript(Subject,To,Cc,Body){
	wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/';
	var res = addItem(sendMailListGuid,{Title:Subject,To:To,Cc:Cc,EmailBody:Body});
	if(!res.success){
		alert(res.errorText);
	}else{
		alert("Sending OK!");
	}
}
</script>

This code inserts a button that calls the function “init_sendMailWithJavascript” and sends a test message. You must modify the variable “sendMailListGuid” to reflect your “send mail” list’s GUID. Refer to the link above the codeblock for instructions. Also modify the “To” and “Cc” addresses.

The function “init_sendMailWithJavascript” is just an example – you can call the function “sendMailWithJavascript” directly from your script.

NOTE: Starting Workflows is not possible for anonymous users (may be possible using a third party fix).

Ask if anything is unclear.

Alexander

Cascading dropdowns in SharePoint text fields – populated by lookup in another list (version 2)

03.11.2010 Updated the code to eliminate the need to refer other external resources than jQuery. Removed the argument “lookupSourceListURL” from the function call.

The code is tested in IE, Chrome and Firefox. Opera is not supported.

NOTE: When updating existing script you must modify the function call and remove the argument “lookupSourceListURL”.


I have previously posted a solution for creating cascading dropdowns from SharePoint single line text fields. Populated based on a query against another list.

I have reviewed the script to add a few enhancements.

  • No more need to create a calculated column in the “lookup list”
  • Less arguments to pass to the function – sleeker code
  • Overcomes a possible bug regarding the previous version using “BeginsWith” rather than “Eq” to match the items

This release (as the previous) has these features:

  • “Converts” standard SharePoint single line text fields to dropdowns
  • Uses the converted SharePoint single line text fields to hold the values
  • Populates the dropdowns by querying any SharePoint list or library
  • Dynamically fills or clears the hidden fields holding the selected value to adapt to changes in the selections – thus preventing “impossible combinations”
  • Preserves selections during page refresh due to form validation
  • Reads back and fills the dropdowns if used in EditForm

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”). Upload jQuery and the file “spjs_CascadingDropDowns_v2.js” to that library.

The jQuery-library is found here. The sourcecode refers to jquery-1.4.2.min. Some of the functions are not supported in previous releases.

The sourcecode for the file “spjs_CascadingDropDowns_v2.js” can be found below.

Read here how to add a CEWP to the NewForm or EditForm, how to find the list Guid of your list, and how to find the FieldInternalName of your columns.

You can pull any values from another list – an example provided below.

Source list:
IMG
The field “Make” is the native “Title” field.

Target list:
IMG

The CEWP code – place below the Form:

<script type="text/javascript" src="/test/English/Javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/spjs_CascadingDropDowns_v2.js"></script>
<script type="text/javascript">
   // Use the FieldInternalName and not the Displayname.
   providerArr = ['Title','carModel','carColor','carInterior','carMilage'];
   consumerArr = ['Title','Model','Color','Year','Milage'];
   cascadingDropDowns_v2(providerArr,consumerArr,'{83eb224b-03fa-4a8b-b493-80253373a962}','<select>',5,true,false);
</script>

Parameters explained:

  • providerArr: Array of FieldInternalNames of the fields in the source-list
  • consumerArr: Array of FieldInternalNames of the fields in the list where the dropdowns will be created
  • lookupSourceListGuid: GUID of the source-list
  • dropDownDefaultvalue: The default value of dropdowns that are not empty – ex. <select>
  • numberOfDropdowns: Number of levels of cascading dropdowns – (2-5)
  • debug: true or false – if true: the textfield that holds the dropdownvalue is visible and some alerts are displayed

The code for the file “spjs_CascadingDropDowns_v2.js” can be found here

How to use these scripts in a customized form

Ask if anything is unclear.

Alexander

Edit document metadata without creating a new version or triggering workflows

Have you noticed that when uploading a new documents in a document library, the connected workflows do not trigger?

This behavior can be used to “sneak” in changes in a documents metadate without triggering new versions or connected workflows.

The trick is simply to load the page with the parameter “&Mode=Upload” in the URL:
[your root address]/test/English/DocumentLibrary/Forms/EditForm.aspx?ID=1&Mode=Upload

Make the changes and click “OK”. The changes are merged into the current version and no workflows are triggered!

Note: “Modified” and “Modified By” is updated.

Alexander

Manipulate upload link in document library

I got this request from JGilmore:

Anyone know how to change the URL of the ‘Upload’ button as well as the links under its dropdown? I would like to have the user directed to a custom upload page with ‘Overwrite existing files’ unchecked by default.

Thanks in advance.


Here is one possible approach

Add this code in a CEWP below the list view to override the default links:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">

// Manipulate direct click on upload button
$("*[id$='_UploadMenu']").parents('td:first').removeAttr('onclick').click(function(){
	redirectUpload(false);
});

// Manipulate "Upload Document"
$("*[id$='_Upload']").attr('onMenuClick','redirectUpload(false)');
		
// Manipulate "Upload Multiple Documents"
$("*[id$='_MultipleUpload']").attr('onMenuClick','redirectUpload(true)');		
		
function redirectUpload(multi){
	// Set your new upload destination (custom upload page)
	// ctx.listName is provided by SharePoint
	if(multi){
		window.location='/test/English/MyCustomUploadPage.aspx?List='+ctx.listName+"&MultipleUpload=1";
	}else{
		window.location='/test/English/MyCustomUploadPage.aspx?List='+ctx.listName;
	}
}
</script>

Change the new upload location in the function “redirectUpload”. Also edit the source of the jQuery script if you prefer to use a local copy.

Ask if anything is unclear.

Alexander




%d bloggers like this: