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 PowerShell

Initially Nintex team has provided details to the in built web service for publishing a workflow.


Above link explains all available methods within the service 

·         Endpoint URL
·         DeleteForm
·         GetFormXml
·         PublishFormXml
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 asPublish 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-NintexFormXML method included to get XML File instead of hardcoded path


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 Download Entire Script from here

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

Post a Comment

Previous Post Next Post