Automating deployment of reusable workflows in Nintex Workflow 2010

One of the options for creating workflows in Nintex Workflow 2010 (NW2010 for short) is to associate workflows  with Content Types.This category of workflows is known as Reusable workflows.

As stated in Nintex Workflow 2010 User Manual

A resusable workflow template allows the workflow to be used on a content type, list or library through the
default SharePoint Workflow settings option.
Reusable workflow templates can be created for use within a single site or an entire site collection

In our case we are only interested in reusable workflows for content types.

Let’s disscuss the scenario when we need to automate the deployment of reusable workflow. It may take place for example, when there are several environments. The approach discussed here allows to deploy workflows during feature activation.

NW2010 API contains publishing infrastucture (Nintex.Workflow.Publishing namespace) that allow to programatically publish workflow templates.
Class Publish contains  method

WorkflowMetaData PublishAWorkflow(string wfName, NWActionConfigurations configs, Guid listId, SPWeb web, ImportContext importCtx, bool validate, SPContentTypeId contentTypeId, string changeNotes)

view raw
PublishAWorkflow.cs
hosted with ❤ by GitHub

that allows to associate workflow using ContentTypeId parameter.

The method below demonstrate how to deploy reusable workflow file by passing it file name, contenttype name and workflow name(NWFMappingEntry parameter)  using Nintex API PublishAWorkflow method.

/// <summary>
/// Publish Reusable Workflow
/// </summary>
/// <param name="mapping"></param>
public void PublishReusableWorkflow(NWFMappingEntry mapping)
{
SPContentType ct = web.ContentTypes[mapping.BindingName];
string workflowName = mapping.WorkflowName;
string pathToNWF = Path.Combine(properties.Definition.RootDirectory, mapping.WorkflowFileName);
byte[] workflowData = File.ReadAllBytes(pathToNWF);
string workflowFile = Utility.ConvertByteArrayToString(workflowData);
while ((int)workflowFile[0] != (int)char.ConvertFromUtf32(60)[0])
workflowFile = workflowFile.Remove(0, 1);
ExportedWorkflowWithListMetdata workflowWithListMetdata = ExportedWorkflowWithListMetdata.Deserialize(workflowFile);
string xmlMessage = workflowWithListMetdata.ExportedWorkflowSeralized;
SPListCollection lists = web.Lists;
Dictionary<string, Guid> dictionary = new Dictionary<string, Guid>(lists.Count);
foreach (SPList spList in lists)
{
if (!dictionary.ContainsKey(spList.Title.ToUpper()))
dictionary.Add(spList.Title.ToUpper(), spList.ID);
}
foreach (var listReference in workflowWithListMetdata.ListReferences)
{
string key = listReference.ListName.ToUpper();
if (dictionary.ContainsKey(key) && !dictionary.ContainsValue(listReference.ListId))
xmlMessage = xmlMessage.Replace(Utility.FormatGuid(listReference.ListId), Utility.FormatGuid(dictionary[key]));
}
var exportedWorkflow = WorkflowPart.Deserialize<ExportedWorkflow>(xmlMessage);
foreach (var config in exportedWorkflow.Configurations.ActionConfigs)
WorkflowRenderer.ProcessActionConfig(config);
Guid listId = Guid.Empty;
bool validateWorkflow = true;
Publish publish = new Publish(web);
publish.PublishAWorkflow(workflowName, exportedWorkflow.Configurations, listId, web, (ImportContext)null, validateWorkflow, ct.Id, string.Empty);
}

Project that demonstrates how to deploy Nintex reusable workflows may be found here

Validate SharePoint People Editor in SharePoint 2010

Overview

Validation of SharePoint PeopleEditor WebControl on the client-side is often required. On server side validation is supported, see AllowEmpty and  ValidatorEnabled properties for details, but the lack of client side validation makes it inconvenient for scenarios where client side validation is mandatory. So let’s discuss some techniques how it could be achieved.

Server-Side Validation

In order to enable/disable validation for Entity Editor based controls (like People Editor) the following properties should be specified in combination:

For example, the  code below demonstrates how to disable empty values:

<wssawc:PeopleEditor
AllowEmpty="false"
ValidatorEnabled="true"
id="userPicker"
runat="server"
SelectionSet="User,SecGroup"
/>

Client-Side Validation

Let’s start with the simple method that allows to check if PeopleEditor control is not empty.

The code below demonstrates  how to validate if SharePoint PeopleEditor control value is not empty

function validateIfPeopleEditorNotEmpty(peId) {
var $pe = $("#" + peId);
var $peValHolder = $pe.find("input[id$='hiddenSpanData']");
return ($peValHolder.val().length > 0);
}

It can be used in the following scenarios:

Since jQuery is commonly used  in SharePoint front-end development, it validation  capabilities could be used  for  PeopleEditor,  for example  jQuery Validation plugin. The code below demonstrates how validation rule that allow us to verify if PeopleEditor value is not empty, may look like.

//People Editor Validator with jQuery Validation plugin
function validatePeopleEditor(peId,messageText)
{
var $pe = $("#" + peId);
var $peValHolder = $pe.find("input[id$='hiddenSpanData']");
$peValHolder.rules("add", {
required: true,
messages: {
required: messageText
}
});
}

References

Initializing SharePoint People Editor using JavaScript in SharePoint 2010

Sometimes SharePoint PeopleEditor WebControl need to be initialized on the client side.

Initialize People Editor control

//Init People Picker
//peoplePickerId – SharePoint People Editor ClientID
//pickerEntityXml – PickerEntity in xml (see method ToXmlData for class PickerEntity)
function initPeoplePickerBox(peoplePickerId, pickerEntityXml) {
var $pe = $("#" + peoplePickerId);
var entityProps = {LoginName: pickerEntityXml.attr("Key"),
UserName: pickerEntityXml.attr("DisplayText"),
Description: pickerEntityXml.attr("Description")};
var entityExtendedProps = pickerEntityXml.find("ArrayOfDictionaryEntry");
var $peData = $pe.find("input[id$='hiddenSpanData']");
var $peDisplayArea = $pe.find("div[id$='_upLevelDiv']");
var displayData = "<span id='span{LoginName}' iscontenttype='true' tabindex='-1' class='ms-entity-resolved' contenteditable='false' title='{LoginName}'>" +
" <div style='display:none;' id='divEntityData' key='{LoginName}' displaytext='{UserName}' isresolved='True' description='{LoginName}'>" +
" <div data='{PickerEntityData}'></div>" +
" </div>" +
" <span id='content' tabindex='-1' contenteditable='false' onmousedown='onMouseDownRw(event);' oncontextmenu='onContextMenuSpnRw(event,ctx);'>{UserName}</span>" +
"</span>";
var pickerEntityData = convertFromXML(entityExtendedProps[0]);
displayData = displayData.replace(/{LoginName}/g, entityProps.LoginName);
displayData = displayData.replace(/{UserName}/g, entityProps.UserName);
displayData = displayData.replace('{PickerEntityData}', pickerEntityData);
$peDisplayArea.append(displayData);
$peData.val(displayData);
}

Clear People Editor control

//Clear People Picker
//peoplePickerId – SharePoint People Editor ClientID
function clearPeoplePickerBox(peoplePickerId) {
var $pe = $("#" + peoplePickerId);
var $peData = $pe.find("input[id$='hiddenSpanData']");
var $peDisplayArea = $pe.find("div[id$='_upLevelDiv']");
$peDisplayArea.children().remove();
$peData.val("");
}