Category Archives: Workflow

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='https://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

Check workflow status and refresh page when status equals “Completed”

11.01.2010: Updated code due to a bug (line 36 is updated). Thanks to Scott Muldowney for spotting it.


The scenario:
You open a list item in EditForm, and the list item has a running (not yet completed) workflow. Nothing prevents you from editing the item, but when you try to save your edited item:

Save Conflict
Your changes conflict with those made concurrently by another user. If you want your changes to be applied, click Back in your Web browser, refresh the page, and resubmit your changes.

In this article i will provide a solution for:

  • Checking the workflow status, and if not finished, set a timer that checks the workflow every 5 seconds for a set period of time.
  • Refresh the page if the workflow finishes within the maximum wait time.

If status is “In progress”, the timer runs, and the status is checked every 5 seconds:
IMG
If timed out, you get this:
IMG

This article is a follow-up on Prevent editing of a list item if the workflow has failed. You have to read that article before continuing with this one.

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 find them here.

Add a CEWP below your EditForm, and add this code:

<div id="customTimerMsg"></div>
<div id="customTimer"></div>
<div id="customTimerCheck"></div>
<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" src="/test/English/Javascript/RefreshWhenWorkflowCompletes.js"></script>
<script type="text/javascript">
  // How long will the timer run before aborting
  maxWaitSec = 20; 
  // Change ListGuid and Workflow "FieldInternalName"
  listNameOrGuid = 'A3749468-0C23-493B-8854-06DF04200B43';
  workFlowInternalName = 'MyWF';
  administratorEmail = 'alexander.bautz@gmail.com';
</script>

Parameters explained:

  • maxWaitSec: The max wait time in seconds
  • listNameOrGuid: Display name or Guid of current list
  • workFlowInternalName: The “FieldInternalName” of the workflow to check
  • administratorEmail: The administrator’s email – for when the workflow has failed

The code for the file “RefreshWhenWorkflowCompletes.js” is found here:

/* Refresh list item when workflow finishes
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * v1.0
 * LastMod: 10.01.2010
 * ---------------------------------------------
 * Include reference to:
 *  jquery - http://jquery.com
 *  interaction.js - http://spjslib.codeplex.com/
 *  stringBuffer.js - http://spjslib.codeplex.com/
 * ---------------------------------------------
 * Call from a CEWP below the list form in EditForm
*/

$(document).ready(function(){
	checkStatusAndRefresh();
});

var SiteTitle = $.trim($(".ms-sitetitle a").text());
var ListName = $.trim($(".ms-pagetitle a").text()); 

function checkStatusAndRefresh(){
var wfStatus = getWorkflowStatus();
	if(wfStatus==3){ // 3 = "Error Occured"
		$("#part1").hide(); // Hide the list form
		$("#part1").before("<div class='ms-sitetitle'>A workflow has failed. You cannot edit this item until this issue is resolved.</div>" + 
			"<div style='padding-left:10px'><a title='Click to send e-mail to an administrator' href='mailto:" + administratorEmail + "?subject=Failed workflow on site: " + SiteTitle + 
			", list: " + ListName + ", itemId: " + getID() + "'>Click to send e-mail to an administrator</a></div>");
	}else if(wfStatus==2){
		$("#part1").hide(); // Hide the list form
		waitingForRefresh = true;				
		customTimer();
	}else{
		if(typeof(waitingForRefresh)!='undefined' && waitingForRefresh){
			refreshPage();
		}
	}
}

function customTimer(s){ 
if(typeof(s)=='undefined')seconds = 0;
if(typeof(accumulated)=='undefined')accumulated = 0;
	if(accumulated<maxWaitSec){
		if(seconds<5){	
			seconds+=1
			$("#customTimerMsg").html("<div class='ms-sitetitle'>The workflow has not yet finished: Waiting for a maximun of " + maxWaitSec + " seconds,<br>checking the workflow every 5 seconds.</div>");
			$("#customTimer").html("<div style='padding-left:10px'>Waiting: " + (accumulated + seconds) + "</div>"); 
			setTimeout(function(){
				customTimer(seconds);
			},1000);
		}else{
			accumulated = accumulated + seconds;
			$("#customTimerCheck").html("<div style='padding:10px;color:red'>Checking WF...</div>").show().fadeOut(1750);
			checkStatusAndRefresh();
		}
	}else{
		$("#customTimerMsg").html("<div class='ms-sitetitle'>The workflow did not finish during the time specified.</div>" + 
		"<div style='padding:10px;font-size:12px'><a href='" + L_Menu_BaseUrl + "'>Return to site "" + SiteTitle + ""</a> | " + 
		"<a href='javascript:refreshPage();'>Wait for " + maxWaitSec + " new seconds.</a></div>");
		$("#customTimer").html(''); 
	}
}

function refreshPage(){
	window.location.reload();
}

function getWorkflowStatus(){
var thisID = getID();
	wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/';
    var item = getItemById(listNameOrGuid,thisID,[workFlowInternalName]); 
    if(item != null){
        return  item[workFlowInternalName];
    }
}

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

Save as “RefreshWhenWorkflowCompletes.js”, and upload to your scriptlibrary as shown above.

Ask if anything is unclear.

Regards
Alexander

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