Using Nintex rich text editor (RTE) control in a Nintex custom workflow action

In this post I will list the steps required to use Nintex rich text editor in a custom workflow activity. Because most of the steps are the same as when using a SingleLineInput control I will focus only on the difference. The difference is in the custom action’s configuration dialog application page. For the rest of the steps (creating the custom activity class, DependencyProperties, adapter class, etc.) you can check the Nintex Workflow SDK where there is also a sample project CustomWorkflowAction.

Steps to use RTE control in the configuration dialog page of a custom Nintex Action:

  1. In your project add reference to Nintex.Workflow.ApplicationPages.dll

  2. In your aspx page add

    <%@ Register TagPrefix=”Nintex” TagName=”RTE” Src=”~/_layouts/15/NINTEXWORKFLOW/RTE.ascx” %>

  3. Add the control

    <Nintex:ConfigurationProperty ID=”ConfigurationProperty8″ runat=”server” FieldTitle=”YourFieldTitle” RequiredField=”True”>

    <TemplateControlArea>

    <Nintex:RTE runat=”server” ID=”RTEControl1″ ApproverContext=”true” HideModeChooser=”True” height=”140″ >

    </Nintex:RTE>

    </TemplateControlArea>

    </Nintex:ConfigurationProperty>

  4. To read the value from the saved configuratioin and fill the control when the form is loaded use this javascript code in the function TPARetrieveConfig():

    setRTEValue(<%= RTEControl1.RTEClientId %>, configXml.selectSingleNode(“/NWActionConfig/Parameters/Parameter[@Name=RTEControl1]/PrimitiveValue/@Value”).text);

  5. To save the value of the control when the form is submitted use this code in the function TPAWriteConfig():

    configXml.selectSingleNode(“/NWActionConfig/Parameters/Parameter[@Name=’RTEControl1‘]/PrimitiveValue/@Value”).text = getRTEValue(<%=RTEControl1.RTEClientId %>);

  6. In the code behind of the page declare the control

    Nintex.Workflow.ApplicationPages.RTE RTEControl1;

The last step is important otherwise Visual studio will declare it automatically in the partial class (YourDialogPage.aspx.design.cs) but it will declare it as System.Web.UI.UserControl.

Because RTE control inherits from UserControl the project can be build and deployed without errors but the javascript code for reading and saving the control’s value will fail in runtime and the following Compilation Error would be thrown:

Error: Compilation Error Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately. Compiler Error Message: CS1061: ‘System.Web.UI.UserControl’ does not contain a definition for ‘RTEClientId’ and no extension method ‘RTEClientId’ accepting a first argument of type ‘System.Web.UI.UserControl’ could be found (are you missing a using directive or an assembly reference?) Source Error: setRTEValue(<%= RTEControl1.RTEClientId %>, configXml.selectSingleNode(“/NWActionConfig/Parameters/Parameter[@Name=’RTEControl1′]/PrimitiveValue/@Value”).text);

 

Delete current minor version of a document

Case

There is a document already published to 1.0 and a user has accidently created a draft version 1.1. This new minor version must be deleted so the document is back to version 1.0.

Investigation

In this particular case the version history contains the following versions: 0.1, 0.2, 0.3, 1.0, 1.1. There is no way to delete versions 1.0 and 1.1 neither using the UI nor programmatically. They both have IsCurrentVersion=True and trying to delete any of these two versions throws an exception clearly stating that a current version of a document cannot be deleted.
After failing to delete it programmatically I tried by unpublishing version 1.0 but that doesn’t solve the problem because version 1.1 is still there.
After some experiments with the library settings I found a workaround to bring the version history as it was before creating the minor version 1.1.

Solution

By temporarily disabling the versioning for the document library I was able to modify and bring the version history as it was before.
This solution only works if there is only one draft version, in our case 1.1. If more minor versions are created (1.2 or more) then this solution will not work.

For demonstration purposes let’s say the version history for a document is: 0.1, 0.2, 0.3, 1.0, 1.1. We want to delete 1.1 and keep the other versions.

Steps:

  1. Disable versioning for the library
  2. Checkout and check-in the document
  3. Turn the versioning back on. Version history now is 0.1, 0.2, 0.3, 1.1
  4. Unpublish the document. Now the Unpublish option is available for 1.1. (After this step the version history is 0.1, 0.2, 0.3, 0.4)
  5. Checkout then Major-check in the document. (After this step the version history is 0.1, 0.2, 0.3, 0.4, 1.0)
  6. Delete the minor version created in step 5 (In our example version 0.4). (The resulting version history: 0.1, 0.2, 0.3, 1.0, i.e. same as before 1.1 has been created)

If the contents of 1.0 and 1.1 are different then before performing the steps above save the file version 1.0 locally and uploaded it to the library after step 4.

On a production environment it’s important to choose a proper time to perform these steps when no user is working with the library otherwise they may create major versions by accident during the time when versioning is off for the library. For more information how temporarily disabling versioning affects a list check my other post Effects of temporarily disabling versioning for a SharePoint document library

If you have event receivers registered for the document library you may want to remove them temporarily if you don’t get the behaviour explained in the steps above. To remove and add back the event receivers for a list check my other post Add, Modify or Delete List Event Receivers with PowerShell

“This file cannot be saved because some properties are missing or invalid”

Error


This file cannot be saved because some properties are missing or invalid.
Use the Document Information Panel to provide the correct property values. Errors for required properties are marked with a red asterisk, and errors for invalid properties are marked with a red dashed border.

Only specific pattern allowed. Only data in the following pattern is allowed: ‘.*\S.*’

Case

There is a document library in SharePoint 2010 site. Users are using Office 2010 32-bit.Saving an open document from client computers doesn’t work for all the client PCs. On some PCs saving the document fails and this error is shown: “This file cannot be saved because some properties are missing or invalid. Use the Document Information Panel to provide the correct property values. Errors for required properties are marked with a red asterisk, and errors for invalid properties are marked with a red dashed border”.
In the Document Information Panel there are no properties marked with red and everything looks fine.

Investigation

There are two cases I discovered that experience this problem and both are caused from the same bug in Office.

Case 1:

Labels are enabled for the content type in the document library and the label specified contains line breaks, for example {Field1}\n{Field2}.
The error occurs even without inserting the label in the document’s content. After removing the line breaks from the label the documents can be saved.

Case 2:

There were some hidden columns in the document library’s content type. I made them Optional and opened a document from a PC that is having the problem. In the Document Information Panel there was one property marked with a red dashed border.

This file cannot be saved because some properties are missing or invalid.

Right clicking and selecting “Full error description…” popped-up the following message: “Only specific pattern allowed. Only data in the following pattern is allowed: ‘.*\S.*’”.

“Only specific pattern allowed.

It turns out that the validation is not allowing line breaks in the property. After removing the line breaks I was able to save the document.

The question now is why the problem was present only on some of the PCs. Checking the Office build version discovered that the problem was present only on client PCs with Office 2010 14.0.7106.5003. Client PCs with Office 2010 without service packs (14.0.476.1000) are not having this problem. This is a bug introduced with Office 2010 updates. Microsoft is aware of this bug and they are working on it and will be fixed with the next Office 2010 updates. It’s been few months since then (October 2013) and the issue is still present. Hopefully they will release the hotfix soon.

Solution

(Bug fixed. Check UPDATE 24/03/2014 at the end of this post.)

Because this is a bug in Office 2010, as a temporary “solution” I uninstalled Office 2010 14.0.7106.5003 and installed a clean Office 2010 14.0.476.1000 without service packs. I’ll stick with 14.0.476.1000 until Microsoft fixes this bug. Hopefully it won’t take too long until they release the hotfix.

UPDATE 22/01/2014:
To find out which update is the problem I performed the following:

-Installed clean Office 2010 (32 bit) 14.0.476.100 – The issue is not present

-Then Installed Service Pack 2 14.0.7015.1000 –The issue is not present

-Then installed the updates after SP2 one by one until the problem started.

Discovered that KB2826026 (08/10/2013) is the update that has introduced this bug.

-To make sure that this is the only problematic update I uninstalled KB2826026 and installed the other latest updates (except KB2826026) and the issue is not present.

Microsoft hasn’t released the fix for this bug yet. Uninstalling KB2826026 solves the problem temporarily until the hotfix is released.

UPDATE 25/02/2014:
“The KB2826026 has been replaced by the KB2837583 (released 11/02/2014) but Microsoft did not remove the bug brought by the first KB. So you have to uninstall the KB2837583 otherwise you will have this bug again …” – visitor’s comment (Nicolas).

In my case uninstalling KB2826026 (now KB2837583) fixed the problem. If in your case that doesn’t solve the problem then try by uninstalling other updates released after September 2013. Check the comments below.

UPDATE 12/03/2014:

KB2837583 (released 11/02/2014) is now replaced with KB2878225(release 11/03/2014) but the bug is still not fixed. So this update (KB2878225) must be uninstalled too.

UPDATE 24/03/2014:

Good news! The bug is fixed with hotfix KB2878228. You have to download and install it manually because its not included with the automatic updates.

Get Timer Job Status programmatically

The status of a running timer job can checked programmatically with SPRunningJob.Status. The status can be in one of the following states: Scheduled , Initialized , Succeeded, Failed, Retry, Aborted, Pausing, Paused.
If a job definition is not running in a given moment then we can’t get it with SPWebApplication.RunningJobs. In that case we have to get the timer job’s history entries and check with what status has the job ended when it’s been executed.

Below is the code to get the status of a timer job with a given name. If the job is not running then it checks the last timer job history entry. One minute schedule is assumed for this example. Modify it as per your time job schedule.

using Microsoft.SharePoint.Administration;

private SPRunningJobStatus CheckTimerJobsStatus(SPWebApplication webApp, String timerJobName)
{
	SPRunningJobStatus status;
	
	try
	{
		SPRunningJob runningJob = null;
		SPRunningJobCollection jobs = webApp.RunningJobs;
		foreach (SPRunningJob rJob in jobs)
		{
			if (runningJob.JobDefinitionTitle == timerJobName)
			{
				runningJob = rJob;
			}
		}

		if (runningJob != null)
		{
			status = runningJob.Status;
		}
		else
		{
			SPJobDefinitionCollection jobDefs = webApp.JobDefinitions;
			SPJobHistory lastJob = GetLastJobFromHistory(jobDefs, timerJobName);
			if (lastJob != null)
			{
				status = lastJob.Status;
			}
		}
	}
	catch (Exception ex)
	{
		//Hand the exception as fit, depending where you run this code
	}

    return status;
}
		
private SPJobHistory GetLastJobFromHistory(SPJobDefinitionCollection jobDefs, string jobName)
{
	SPJobHistory lastJob = null;
	foreach (SPJobDefinition jobDef in jobDefs)
	{
		if (jobDef.DisplayName == jobName)
		{
			IEnumerable<SPJobHistory> jobHistory = jobDef.HistoryEntries;

			foreach (SPJobHistory jobHistoryItem in jobHistory)
			{
				if (jobHistoryItem.StartTime - jobDef.LastRunTime < new TimeSpan(0, 1, 0))
				{
					lastJob = jobHistoryItem;
					break;
				}
			}
		}
	}
	return lastJob;
}

The account that executes this code should have enough rights on the configuration database otherwise you will get the following error

The EXECUTE permission was denied on the object ‘proc_GetTimerRunningJobs’, database ‘SharePointConfigDBName’, schema ‘dbo’

The farm account already have the required permissions but you may get this error if you run this with another account, even if you run it with elevated privileges.
For example if you run this code in an application page with RunWithElevatedPrivileges block you may get this error because RunWithElevatedPrivileges elevates the current account to the application pool account, but that account may not have enough rights to execute those SQL procedures.
In that case you have two options: 1. impersonate the farm account or 2. go to the configuration database and grand the application pool account EXECUTE permission for the procedures required for this code to run. EXECUTE permission is needed for the following stored procedures in the configuration database:

  • proc_GetTimerRunningJobs
  • proc_GetTimerJobHistory
  • proc_GetTimerJobLastRunTime

Because we should not directly modify the content and configuration databases its better to impersonate the farm account instead of touching the configuration database.

%d bloggers like this: