Propagating legacy Document IDs into SharePoint

Document IDs in SharePoint

Document IDs are generated automatically by SharePoint when configured, but what if you have your own legacy Document IDs that you want to use? There is a way to push them in.

The first trick is recognizing there are two internal fields created when Document IDs are enabled.
ows__dlc_DocId : The actual Document ID
ows__dlc_DocIdUrl : The special Redir.ASPX based URL with the document ID within it

Let’s examine the later field in a bit more detail. This contains a URL with a reference to “DocIdRedir.aspx” which is an ASPX ghosted page that will redirect either via local service lookup, or a fallback to a search based lookup. Here’s what it looks like:

http ://server/_layouts/DocIdRedir.aspx?ID=DOC-1000-3109, DOC-1000-3109″

Note the comma, then the repeated Document ID.

Now imagine if there’s a field in our document library that has the legacy Document ID we want to use.

The script below goes through, and systematically replaces the existign Document ID with the Legacy one we want to use.

[system.reflection.assembly]::LoadWithPartialName("Microsoft.Sharepoint") 
Write-Host ("$(get-date) Running script to assing DOcIDs")
$SiteFilter = "*"
$siteUrl = "http ://SharePoint" #use your own Web App URL
$ThisLibOnly = $null; #allows for filtered selection of libraries
Write-Host ("$(get-date) Assigning DocIDs from Legacy IDs")
$webApp=Get-SPWebApplication $SiteURL
$Sites = $webApp | Get-SPSite -Limit all
foreach ($site in $Sites)
{
$OutputSuffix=$loopLetter;
$mylogfile="C:Tempoutput.log"
write-host $site.Url
Write-Host -foregroundcolor darkblue "$($site.id) - $($site.Url) - $($site.contentdatabase.id) - $($site.contentdatabase.name)"   
$WebScope = Start-SPAssignment
foreach ( $TargetWeb in $site.AllWebs)
{
Write-Host("Working in site: " + $TargetWeb)
$ListOfLists = @();
# Loop through all doc libs
$lists=$TargetWeb.lists;
$listsCount=$lists.count
for ($ii=0; $ii -lt $listsCount; $ii++)
{
$JPlib=$lists[$ii];
if ( ($JPlib.BaseType -ne "DocumentLibrary") -or ($JPlib.hidden) )
{
# forget the rest and return to top
Write-Host -foregroundcolor darkred "fast test skipping Library: $($JPlib)";   
}
elseif ($JPLib.Title -Match "Photo|Image|SitesAssets|CustomizedsReports|Templates|Pages|Picture|cache|style")
{
# forget the rest and return to top
Write-Host -foregroundcolor darkred "fast test skipping Library because it mentions $Matches: $($JPlib)";   
}
elseif (!$JPlib.contenttypesenabled)
{
continue; # no content types, no interest
}
else
{  
Write-Host -foregroundcolor green "Processing Library: $($JPlib)";   
$ListOfLists += $JPlib.title;
}
}
foreach ($CurrentLib in $ListofLists)
{
$JPlib=$TargetWeb.lists[$CurrentLib];
$JPlib.title
if ($JPlib -eq $null)
{
Write-Host "COULD NOT GET LIB $($CurrentLib)"
continue;
}
Write-Host -foregroundcolor green "Processing Library: $($JPlib) in $($TargetWeb)";   
if (($ThisLibOnly -eq $null) -or
($JPlib.title -eq $ThisLibOnly)) 
{
$JPItems=$JPlib.Items;
$JPCount=$JPItems.get_count();
if ($JPCount -eq 0) {continue} #can't do much on a library with no items!
for ($i=0; $i -lt $JPCount; $i++)  #Do not use ItemCount, that one includes folders!
{	
$JPItem=$JPItems[$i];
$SourceValue=$JPItem["LegacyDocID"];
if (($SourceValue -eq $null) -or ($SourceValue.length -le 0))
{
write-host "-" -nonewline
continue; #nothing to assign
}
elseif ($JPItem["ows__dlc_DocId"] -ne $SourceValue) #avoid reassigning same value
{
Write-Host "Old DocID=$($JPItem['ows__dlc_DocId']),LegacyDocID=$($SourceValue)"
$oldDocIDValue=$JPItem["ows__dlc_DocId"]
$JPItem["ows__dlc_DocId"] = $SourceValue;
if ($JPItem["ows__dlc_DocIdUrl"].length -gt 1)
{
$JPItem["ows__dlc_DocIdUrl"]= $JPItem["ows__dlc_DocIdUrl"].replace($oldDocIDValue,$SourceValue);
}
$JPItem.systemupdate() #without generating version
Write-Host "$($i): $($JPItem.url)"
}
else
{  #special!  $JPItem["ows__dlc_DocIdUrl"]=$null;
Write-Host "DOcID Match! $($JPItem.url)"
Write-Host "Old DocID=$($JPItem['ows__dlc_DocId']),LegacyDocID=$($SourceValue)"
}
}		
}
Write-Host "+" -NoNewline
}# Lib loop
$JPlib.Update();
$JPlib=$null;
} #all libs?
try
{
$TargetWeb.dispose()
} catch{}
$TargetWeb=$null;
Stop-SPAssignment $WebScope
} #all webs?
} # all sites?
$site.Dispose()
$site=$null;
Stop-SPAssignment $SiteScope
Write-Host "SCRIPT COMPLETE $(get-date)"

Structure Document IDs to be unique across Site Collections

Configure Document IDs to be unique across the farm

Document IDs are only guaranteed unique within a single site collection. SharePoint tries to ensure uniqueness by putting a random prefix in front of each Docuemnt ID, and setting that at the Site Collection level. However you can easily rationalize these, and make the Document IDs a bit easier to read. The script below assigns a prefix, and sets up SharePoint to reissue Document IDs. Note the actual regeneration of Document IDs will wait until the Document ID Assignment Timer Job runs. This job can take a long time to run, depending on the number of items in your Site Collections.

[system.reflection.assembly]::LoadWithPartialName("Microsoft.Office.DocumentManagement") 
$siteUrl = "http ://sharepoint/ManagedPath"  #this is the header prefixing my Site Collections
$LoopString = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"  #Here are the individual Site Collections
$LoopStringArr = $LoopString.Split(“,”)
foreach ($letter in $LoopStringArr)
{
$SiteName=$siteurl+$letter
write-host $SiteName
$Site = New-Object Microsoft.SharePoint.SPSite($SiteName)
[Microsoft.Office.DocumentManagement.DocumentID]::EnableAssignment($Site,$false)   #First disable, then enable DocID assignment
[Microsoft.Office.DocumentManagement.DocumentID]::EnableAssignment($Site,$true)
$rootweb=$site.rootweb
$rootweb.properties["docid_msft_hier_siteprefix"]="Ins$letter"  # This is the property holding the Document ID Prefix which we use to ensure uniqueness
$rootweb.properties.Update()
$rootweb.Update()
[Microsoft.Office.DocumentManagement.DocumentID]::EnableAssignment($Site,$true,$true,$true)  # now we can force all Document IDs to be reissued
}

For more information on the methods used above, please reference MSDN