Publishing Nintex form through PowerShell, was a challenge for me during last few weeks. After consulting Nintex team they have provided many valuable information related to the same topic.I will go through each and every information which I have gathered. Finally I was able to publish the Nintex Form through PowerShellInitially Nintex team has provided details to the in built web service for publishing a workflow.Nintex Web Service ReferenceAbove link explains all available methods within the service
The moment I tried to access the web service via browser, I got an error saying “End Point Not Found“.
But later I found a valuable post on Nintex community by mattbriggs who has overcome same issue through Visual Studio Solution in his blog post Named as “Publish a Form Using the Nintex Forms On Prem 2013 Web Service“.
Unfortunately in my corporate environment as a policy we are not publishing any custom build WSP solutions in to our farm.
In the Same page sonisick has provided an amazing solution to implement the same in PowerShell.
I was able to use same functions as it was and implemented a new function to get XML Form downloaded to a custom folder.
All credits should go to sonisick and mattbriggs
I’m publishing their methods along with the additional form download functionality which I was implemented using their references.
PowerShell Code Function Definitions
If I say reused method, that means it was copied from reply of sonisick from the above specified blog post
Function Name | Purpose |
Create-Folder | If not exists, Create a new folder to hold downloaded XML file |
DecodeXMLString | Decode serialized JSON output in to XML string |
Nintex-PublishFormToNewList | Publish the XML form in to destination SharePoint List (reused same method from Sonisick’s reply ) |
Get-FormDigest | Reused method |
Nintex-GetJsonFormFromXml | Reused Method |
Read-FileBytes | Reused Method |
Get-SPListBylistName | This is modified method from above Post to get SPList object from List name (instead of internal name of the list) |
checkFileExists | Used to check XML file already generated or not for same list |
Get-NintexFormXML | Implemented to download the XML File from the List using GetFormXML web service method. |
Publish-NintexForm | Reused Method – Get-NintexFormXM |
Again Please Note Below Solution I have created as part of Creating Sub Sites and Publishing their Nintex Forms via PowerShell. Some of the parameters might not required for you to use within this Script
Creating Sub Sites and Setting Master Page through PowerShell, I have discussed on my previous Post
To Publish the Nintex Form you can use the function as follows
Publish-NintexForm -templateSiteURL $sourceSiteURL -destinationSiteURL $subSiteUrl -listNameToPublishForm $listNameToPoblushForm -FolderPath $xmlFileFolderPath -initiatorCredentials $Credentials
Full Script is as follows
#Form Publish Entire Methods : http://www.get-spnote.com/2018/02/how-to-publish-nintex-forms-through.html
#Form Publish Sample #https://community.nintex.com/community/dev-talk/blog/2016/05/13/publish-a-form-using-the-nintex-forms-on-prem-2013-web-services
Add-PSSnapin Microsoft.SharePoint.PowerShell
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") > $null
$siteURL = "http://sitecollectionURL"; #site collection URL
$templateName = "Project_Template-30-Jan-2018" #Template Name
$numberOfSites = 1; #Number of Sites to Generate
$sourceSiteURL ="http://sitecollectionURL/Project-111"
$Credentials = Get-Credential;
$siteNamePrefix = "Project-";
$subSiteInventoryListName = "Subsites_Inventory";
$masterPage = "/_catalogs/masterpage/MasterPage/Project_Master.master";
$systemMasterPage = "/_catalogs/masterpage/MasterPage/Project_Master_System.master";
$timeStamp = Get-Date;
$startTime =$timeStamp.ToString("yyyy-MM-dd-HH-mm:s");
$filePrefix=$timeStamp.ToString("yyyy-MM-dd-HH-mm");
$ExecutionID=$timeStamp.ToString("yyyyMMddHHmm");
$logFilePath = "E:\Scripts\Project-CreateSubSites\logs\$filePrefix-Log.log";
$siteDNS= "http://sitecollectionPublicURL";
$xmlFileFolderPath ="E:\Scripts\Project-CreateSubSites\XMLS\$ExecutionID";
$xmlFileNewPath ="";
#set Header
Add-Content -Path $logFilePath -value "SiteName | SiteURL |Status | Summary";
#region -- Create New Folder if not Exists
function Create-Folder
{
param ($folderPath)
If(!(test-path $folderPath))
{
New-Item -ItemType Directory -Force -Path $folderPath
}
}
#endregion
#region -- Decode XML String ..
function DecodeXMLString($strEncode)
{
$decodedString ="";
if($strEncode -ne $null)
{
$decodedString= [System.Web.HttpUtility]:: HtmlDecode($strEncode)
}
else
{
$decodedString=""
}
return $decodedString;
}
#endregion
#region Publish Form To New List...
function Nintex-PublishFormToNewList(
[Microsoft.SharePoint.SPWeb] $Web,
[string] $DestinationListName,
[string] $FileName,
[System.Management.Automation.CredentialAttribute()] $Credentials
)
{
$formDigest = Get-FormDigest -Web $Web
$addressUrl = [Microsoft.SharePoint.Utilities.SPUtility]::ConcatUrls($Web.Url, "_vti_bin/NintexFormsServices/NfRestService.svc/PublishForm")
$addressUri = New-Object System.Uri($addressUrl)
# Create the web request
[System.Net.HttpWebRequest] $request = [System.Net.WebRequest]::Create($addressUri)
# Add authentication to request
$request.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
# Set type to POST
$request.Method = "POST";
$request.ContentType = "application/json; charset=utf-8";
$request.Accept = "application/json, text/javascript, */*; q=0.01"
$request.Headers.Add("X-RequestDigest", $formDigest);
$request.Headers.Add("X-Requested-With", "XMLHttpRequest")
$form = Nintex-GetJsonFormFromXml -FileName $FileName
# Create the data we want to send
$list = Get-SPListBylistName -Web $Web -listName $DestinationListName
$id = "{$($list.ID)}"
$data = "{`"contentTypeId`": `"`", `"listId`": `"$id`", `"form`": $form }"
# Create a byte array of the data we want to send
$utf8 = New-Object System.Text.UTF8Encoding
[byte[]] $byteData = $utf8.GetBytes($data.ToString())
# Set the content length in the request headers
$request.ContentLength = $byteData.Length;
# Write data
try {
$postStream = $request.GetRequestStream()
$postStream.Write($byteData, 0, $byteData.Length);
}
catch [Exception]{
write-host -f red $_.Exception.ToString()
}
finally {
if($postStream) { $postStream.Dispose() }
}
# Get response
try {
[System.Net.HttpWebResponse] $response = [System.Net.HttpWebResponse] $request.GetResponse()
# Get the response stream
[System.IO.StreamReader] $reader = New-Object System.IO.StreamReader($response.GetResponseStream())
try {
$strResult = $reader.ReadToEnd()
$jsonResult = ConvertFrom-Json $strResult
}
catch [Exception] {
write-host -f red $_.Exception.ToString()
}
}
catch [Exception] {
write-host -f red $_.Exception.ToString()
}
finally {
if($response) { $response.Dispose() }
}
return $true
}
#endregion Publish form to new list end
#region Get Form Digest....
function Get-FormDigest(
[Microsoft.SharePoint.SPWeb] $Web
)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.IO") >> $null
$formDigestRequest = [Microsoft.SharePoint.Utilities.SPUtility]::ConcatUrls($Web.Site.RootWeb.Url, "_api/contextinfo")
$formDigestUri = New-Object System.Uri($formDigestRequest)
$credCache = New-Object System.Net.CredentialCache
$credCache.Add($formDigestUri, "NTLM", [System.Net.CredentialCache]::DefaultNetworkCredentials)
$spRequest = [System.Net.HttpWebRequest] [System.Net.HttpWebRequest]::Create($formDigestRequest)
$spRequest.Credentials = $credCache
$spRequest.Method = "POST"
$spRequest.Accept = "application/json;odata=verbose"
$spRequest.ContentLength = 0
[System.Net.HttpWebResponse] $endpointResponse = [System.Net.HttpWebResponse] $spRequest.GetResponse()
[System.IO.Stream]$postStream = $endpointResponse.GetResponseStream()
[System.IO.StreamReader] $postReader = New-Object System.IO.StreamReader($postStream)
$results = $postReader.ReadToEnd()
$postReader.Close()
$postStream.Close()
#Get the FormDigest Value
$startTag = "FormDigestValue"
$endTag = "LibraryVersion"
$startTagIndex = $results.IndexOf($startTag) + 1
$endTagIndex = $results.IndexOf($endTag, $startTagIndex)
[string] $newFormDigest = $null
if (($startTagIndex -ge 0) -and ($endTagIndex -gt $startTagIndex))
{
$newFormDigest = $results.Substring($startTagIndex + $startTag.Length + 2, $endTagIndex - $startTagIndex - $startTag.Length - 5)
}
return $newFormDigest
}
#endregion
#region Get Nintex Json Form XML..
function Nintex-GetJsonFormFromXml($FileName)
{
[System.Reflection.Assembly]::LoadWithPartialName("Nintex.Forms.SharePoint") >> $null
[System.Reflection.Assembly]::LoadWithPartialName("Nintex.Forms") >> $null
[byte[]] $fileBytes = Read-FileBytes -FileName $FileName
try
{
$form = [Nintex.Forms.FormsHelper]::XmlToObject([Nintex.Forms.NFUtilities]::ConvertByteArrayToString($fileBytes))
}
catch [Exception]
{
$form = [Nintex.Forms.FormsHelper]::XmlToObject([Nintex.Forms.NFUtilities]::ConvertByteArrayToString($fileBytes, [System.Text.Encoding]::UTF8))
}
$form.LiveSettings.Url = ""
$form.LiveSettings.ShortUrl = ""
$form.RefreshLayoutDisplayNames()
$form.Id = [guid]::NewGuid()
$json = [Nintex.Forms.FormsHelper]::ObjectToJson($form);
return $json;
}
#endregion
#region Read File Bytes....
function Read-FileBytes($Filename)
{
try {
[system.io.stream] $stream = [system.io.File]::OpenRead($Filename)
try {
[byte[]] $filebytes = New-Object byte[] $stream.length
[void] $stream.Read($filebytes, 0, $stream.Length)
return $filebytes
}
finally {
$stream.Close()
}
}
catch {
return
}
return $true;
}
#endregion Read File Bytes
#region Get ShaerPoint List by Internal Name ..
function Get-SPListBylistName($Web, $listName)
{
$list = $Web.Lists["$listName"];
return $list;
}
#endregion
#region CheckFileExists ..
function checkFileExists([string]$path)
{
$found = $false;
if (($path -ne $null) -and ($path.Length -gt 0))
{
if (Test-Path -LiteralPath $path)
{
$found = $true;
}
}
return $found;
}
#endregion
#region Publish Nintex Form....
function Publish-NintexForm
{
[CmdletBinding()]
[OutputType([int])]
Param
(
$templateSiteURL,
$destinationSiteURL,
$listNameToPublishForm,
$initiatorCredentials,
$FolderPath
)
Begin
{
Set-Location $PSScriptRoot
}
Process
{
try
{
$FullFormFileNamewithPath= "$FolderPath\$listNameToPublishForm-Form.xml";
$xmlGenerationOutput =$false;
$sourceweb=Get-SPWeb $templateSiteURL
$destinationWeb=Get-SPWeb $destinationSiteURL
If(!(test-path $FullFormFileNamewithPath)) #XML File does not exist need to recreate
{
$xmlGenerationOutput= Get-NintexFormXML -TemplateWebURL $templateSiteURL -DestinationListName $listNameToPublishForm -FolderPath $FolderPath -Credentials $initiatorCredentials
}
Nintex-PublishFormToNewList -Web $destinationWeb -DestinationListName $listNameToPublishForm -FileName $FullFormFileNamewithPath -Credentials $initiatorCredentials
}
catch [System.Exception]
{
write-host -f red $_.Exception.ToString()
Write-Host -f yellow ("Publish of OCR Form Failed--Please Import and Publish the OCR Form Manually after the script completes")
}
finally
{
if ($sourceweb){
$sourceweb.Dispose()
}
if ($destinationWeb){
$destinationWeb.Dispose()
}
}
}
End
{
}
}
#endregion
#region Get Nintex form Downloaded...
<#This will download the Nintex Form XML file using GetFormXML method in the Web Service#>
function Get-NintexFormXML(
$TemplateWebURL,
[string] $DestinationListName,
[string] $FolderPath,
[System.Management.Automation.CredentialAttribute()] $Credentials
)
{
$Web=Get-SPWeb $TemplateWebURL
$addressUrl = [Microsoft.SharePoint.Utilities.SPUtility]::ConcatUrls($Web.Url, "_vti_bin/NintexFormsServices/NfRestService.svc/GetFormXml")
$addressUri = New-Object System.Uri($addressUrl)
# Create the web request
[System.Net.HttpWebRequest] $request = [System.Net.WebRequest]::Create($addressUri)
# Add authentication to request
$request.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
# Set type to POST
$request.Method = "POST";
$request.ContentType = "application/json; charset=utf-8";
$request.Accept = "application/json, text/javascript, */*; q=0.01"
$request.Headers.Add("X-Requested-With", "XMLHttpRequest")
# Create the data we want to send
$list = Get-SPListBylistName -Web $Web -listName $DestinationListName
$id = "{$($list.ID)}"
$data = "{`"contentTypeId`": `"`", `"listId`": `"$id`" }"
# Create a byte array of the data we want to send
$utf8 = New-Object System.Text.UTF8Encoding
[byte[]] $byteData = $utf8.GetBytes($data.ToString())
# Set the content length in the request headers
$request.ContentLength = $byteData.Length;
# Write data
try {
$postStream = $request.GetRequestStream()
$postStream.Write($byteData, 0, $byteData.Length);
}
catch [Exception]{
write-host -f red $_.Exception.ToString()
}
finally {
if($postStream) { $postStream.Dispose() }
}
# Get response
$xmlFilePath ="";
try {
[System.Net.HttpWebResponse] $response = [System.Net.HttpWebResponse] $request.GetResponse()
# Get the response stream
[System.IO.StreamReader] $reader = New-Object System.IO.StreamReader($response.GetResponseStream())
try {
$strResult = $reader.ReadToEnd()
$xml = DecodeXMLString -strEncode $strResult;
#Once Deserialized XML will get generated with below string tag, insted of going for XML manupulation I have removed the string tag from the xml
$xmlExcludedStringTag = $xml -replace "<string xmlns=""http://schemas.microsoft.com/2003/10/Serialization/"">" , "";
$xmlExcludedStringTag = $xmlExcludedStringTag -replace "</string>" , "";
# Create new Folder if not exist
Create-Folder -folderPath $FolderPath;
# Get XML New File path
$xmlFilePath = "$FolderPath\$DestinationListName-Form.xml";
#Save XML File to Disk
$xmlExcludedStringTag | Out-File $xmlFilePath;
$xmlFileNewPath = $xmlFilePath;
$xmlFilePath ="";
}
catch [Exception] {
write-host -f red $_.Exception.ToString()
}
}
catch [Exception] {
write-host -f red $_.Exception.ToString()
}
finally {
if($response) { $response.Dispose() }
}
return $true;
}
#endregion Get Nintex form Downloaded - end
Publish-NintexForm -templateSiteURL $sourceSiteURL -destinationSiteURL $subSiteUrl -listNameToPublishForm $listNameToPoblushForm -FolderPath $xmlFileFolderPath -initiatorCredentials $Credentials
PublishNintexForms.ps1.txt
Displaying PublishNintexForms.ps1.txt.
Download Entire Script from here
Again Special Thanks should go to sonisick and mattbriggs
Hope this might help someone.
Cheers
Tharindu