How to Publish Nintex Forms through PowerShell

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 NamePurpose
Create-FolderIf not exists, Create a new folder to hold downloaded XML file
DecodeXMLStringDecode serialized JSON output in to XML string
Nintex-PublishFormToNewListPublish the XML form in to destination SharePoint List (reused same method from Sonisick’s reply )
Get-FormDigestReused method
Nintex-GetJsonFormFromXmlReused Method
Read-FileBytesReused Method
Get-SPListBylistNameThis is modified method from above Post to get SPList object from List name (instead of internal name of the list)
checkFileExistsUsed to check XML file already generated or not for same list
Get-NintexFormXMLImplemented to download the XML File from the List using GetFormXML web service method.
Publish-NintexFormReused 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

Leave a Reply

Your email address will not be published. Required fields are marked *