missing-custom-forms.jpg

Missing Custom Forms in SharePoint after Migration

Caroline Sosebee is a Software Engineer at ThreeWill. She comes to us with 20+ years of software development experience and a broad scope of general IT support skills.

Missing Custom Forms Background

I recently got to solve another ‘fun’ problem for one of our clients while doing post-migration support for a SharePoint 2013 to 2016 on-premises move. We were also setting up an Application Lifecycle Management (ALM) process as we went along, so there were 4 environments we used during the migration – Dev, Test (to be used for QA), Stage (to be used for UAT) and Production. This proved fortunate as I don’t think I could have resolved this without being able to look backward at one of the previous servers while troubleshooting.

The problem centered around missing custom forms used in libraries utilizing custom document sets. It first appeared when we went to add a new document set to one of these libraries. Instead of our pretty custom form, it just showed the Title field and the Save and Cancel buttons. The odd thing is that this was only happening in the Stage environment.

Since this problem hadn’t occurred in the other environments (Dev or Test) we quickly determined it was caused by something hinky happening during a test of our incremental migration runs (using a 3rd party tool) which we ran against Stage only (and not Dev or Test). Apparently, it did something behind the scenes to the list schema and impacted two areas in particular: the custom form url association and the default form template values. And no, this was not apparent at first glance for me. It took me a moment (or 10 or 1000) to finally figure out the missing pieces and get it resolved. I am still not very savvy on the ins and outs of custom form associations but will try to explain the best I can.

The default content types for lists and libraries (and all the others I’m sure) have rendering templates for their New/Edit/Display forms. For lists, it is the ‘ListForm’ template while for document libraries it is the ‘DocumentLibraryForm’ template. Document set content types are a little different in that they utilize two different templates. For Edit and Display, they use ‘ListForm’ while for New they use ‘DocSetDisplayForm’. Document sets also have a default form url association (which lists and libraries do not have) called ‘NewDocSet.aspx’. This is assigned to the NewFormUrl value.

You can see these values via a browser REST call when querying the OOTB Document Set content type (or list or library) and you will need to know the GUID of the content type you want to check. There are several different ways to find this value, but I tend to use SharePoint Designer for this. The below REST call is using the GUID for the default document set content type:

{your siteurl}/_api/web/contenttypes/getbyid(‘0x0120D520’)

When looking at the results, you will find the areas in question in two spots. One is relatively near the top, just before the <SchemaXml> node. Then if you scroll all the way down to the bottom of the SchemaXml node you will find the full details.

For our custom document set content types, I knew we utilized custom forms, so the first thing I did was go into SharePoint Designer and check this value. I drilled down to the list and into its content types and then I compared the values between the working environment and the broken one. I found that the reference to our form was missing in the Stage (bad) environment, so I quickly added it back hoping the problem would then be resolved.

Unfortunately, it wasn’t. Instead, I now had the Title field and an extra set of the Save and Cancel buttons displaying. Very, very strange.

The second and trickiest part of this little problem – missing template references – was much harder for me to find. It was thanks to a random comment by a co-worker (about the ‘show in new form’ flag) and a little lucky search juju that finally helped me get to the bottom of it.

This is when I learned about the New/Edit/Display form templates I referenced earlier and that they might be causing the problem. I also learned that I could see these values via PowerShell as well as via REST. The earlier screenshots show the values returned via a browser REST call. Here is how to reference the fields when querying via PowerShell and CSOM (these assume you already have a reference to the content type):

$contentType.DisplayFormTemplateName
$contentType. EditFormTemplateName
$contentType. NewFormTemplateName

Once I knew what to look for, I ran a PowerShell script against both the ‘bad’ and the ‘good’ sites and the problem became abundantly clear. These values had been wiped out in the ‘bad’ site. In the below picture, you will see the ‘good’ values I pulled from Test and then the ‘bad’ values I pulled from Stage. You’ll also note that I’m displaying these values for all content types on the library and can see that not only were the values missing for my custom content type (‘Refund’) but also for the OOTB ‘Document’ content type.

I then modified the script so that it would update these fields to their proper values and it was all magic from there. The document set forms immediately started displaying correctly.

I’ve included the script here for your own reference. You can use this to both verify current settings and to update to new ones. In addition to passing in your site and credentials, you will need to pass in the display name of the list or library to check and a flag that indicates if it should actually update the values or not. It is a fairly simple script overall. Feel free to modify to suit your needs.

One interesting thing I noticed while putting this blog together is that you cannot set these values back to blanks once they have a legitimate value in them. I was trying to do this so I could get fresh screen grabs for this article, but it would not work. Glad I still had the pics from before! 😊

I hope this helps someone and saves some time in the future. Blessings!

update-ct-schema.ps1

[CmdletBinding()]

param(

[Parameter(Mandatory = $false)]

[string]$siteUrl,

[Parameter(Mandatory = $false)]

[string]$user,

[Parameter(Mandatory = $false)]

[string]$pwd,

[Parameter(Mandatory = $false)]

[string]$listDisplayName,

[switch]$update

)

# How to call:

# ./update-ct-schema.ps1 "{site url}" "{userid}" "{pwd}" "{List/library}" -update

#

# Leave off the -update if you just want to see what the current values are, then add it when ready to actually update.

# There are hard coded content types being checked for in this script. Be sure to adjust based on your needs.

# In this version of the script you can feed in the list name but still need to hardcode the content type names so

# be sure to adjust based on your needs.

#

Set-StrictMode -Version "3.0"

Add-Type -Path (Join-Path $PSScriptRoot "libs\Microsoft.SharePoint.Client.dll")

Add-Type -Path (Join-Path $PSScriptRoot "libs\Microsoft.SharePoint.Client.Runtime.dll")

function Main {

[CmdletBinding()]

param()

&nbsp;

Write-Host "$($PSCommandPath): $($siteUrl) [update: $($update.IsPresent)]"

$ctx = GetContext

FixSchema $ctx $update.IsPresent

}

function GetContext {

[CmdletBinding()]

param()

&nbsp;

# Get the client context to SharePoint

$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)

$securePwd = ConvertTo-SecureString $pwd -AsPlainText -Force

$cred = New-Object PSCredential($user, $securePwd)

$ctx.Credentials = $cred

&nbsp;

return $ctx

}

function FixSchema {

[CmdletBinding()]

param($ctx, $applyUpdate)

&nbsp;

try {

$list = $ctx.Web.Lists.GetByTitle($listDisplayName)

$contentTypes = $list.ContentTypes

$ctx.Load($list)

$ctx.Load($contentTypes)

$ctx.ExecuteQuery()

foreach ($contentType in $contentTypes) {

Write-Host "$($contentType.Name): DisplayFormTemplateName = [$($contentType.DisplayFormTemplateName)]"

Write-Host "$($contentType.Name): EditFormTemplateName = [$($contentType.EditFormTemplateName)]"

Write-Host "$($contentType.Name): NewFormTemplateName = [$($contentType.NewFormTemplateName)]"

$needsUpdate = $false

if ($contentType.Name -ne "Folder" -and $contentType.DisplayFormTemplateName -eq "") {

$contentType.DisplayFormTemplateName = "DocumentLibraryForm"

$contentType.EditFormTemplateName = "DocumentLibraryForm"

$contentType.NewFormTemplateName = "DocumentLibraryForm"

$needsUpdate = $true

}

if ($contentType.Name -eq "CT1" -or $contentType.Name -eq "CT2" -or $contentType.Name -eq "CT3") {

$contentType.DisplayFormTemplateName = "ListForm"

$contentType.EditFormTemplateName = "ListForm"

$contentType.NewFormTemplateName = "DocSetDisplayForm"

$needsUpdate = $true

}

&nbsp;

if ($needsUpdate -eq $true -and $applyUpdate) {

Write-Host "Updating CT"

$contentType.Update($false)

$ctx.ExecuteQuery()

}

}

}

catch {

Write-Host $_ -ForegroundColor Red

}

}

main


Related Posts

Caroline SosebeeMissing Custom Forms in SharePoint after Migration