In SharePoint Online, for a list or document library, it is possible to define default metadata values based on the folder where an item or document is added.

As with a lot of cases in SharePoint, the defaults are set on subfolders with inheritance. On every folder-level it is possible to break the inheritance and define other default values for your metadata.

In the example below we work with a Managed Metadata field, called customer, we need to default. This way our co-workers don’t need to specify the value everytime they create or upload a document to the document library.

There are two ways to make the magic happen. Through the user interface, and with Powershell.

User interface

First navigate to the document library settings page and select “Column default value settings”.

On this page you get an overview of all the metadata you can define default values for.

Click on the desired metadata field you want to define the default value for.

When you proceed to upload a document in the library, the default value is automatically set, as configured above.

Powershell

When we need to set these default values for multiple libraries in multiple sites, we prefer to configure this with Powershell.

When looking into the technical aspect of these default values, we noticed three things:

  1. The default values are stored in the Forms-folder in a file called “client_LocationBasedDefaults.html”, which contains an xml-markup of the settings.
  2. This file does not exist when no default values have been set.
  3. An event receiver is added to set the default values when a file is added to the library.

If the file does not exist yet, we need to create an empty file with only the following tag in it:

<MetadataDefaults></MetadataDefaults>

Save this file and name it “client_LocationBasedDefaults.html”.

If the file already exists in the library, you can download it with powershell:

function downloadColumnDefaultsXML{
	param (
			[Parameter(Mandatory=$true,Position=1)]
			[object]$context,
			[Parameter(Mandatory=$true,Position=2)]
			[object]$list
		)
	
	try{	
		$formsFolder = $list.RootFolder.Folders.GetByUrl("Forms")
		$context.Load($formsFolder)
		$context.ExecuteQuery()
	 
		$LocationBasedDefaultsXML = $formsFolder.Files.GetByUrl('client_LocationBasedDefaults.html')
		$context.Load($LocationBasedDefaultsXML)
		$context.ExecuteQuery()
	 
		$fileInfo = [Microsoft.SharePoint.Client.File]::OpenBinaryDirect($context, $LocationBasedDefaultsXML.ServerRelativeUrl)
		$fstream = New-Object System.IO.FileStream("client_LocationBasedDefaults.html", [System.IO.FileMode]::Create);
		$fileInfo.Stream.CopyTo($fstream)
		$fstream.Flush()
		$fstream.Close()
		
	    write-host "info: Download of locationbaseddefaults successful" -foregroundcolor green
    }
    catch{
        write-host "info: $($_.Exception.Message)" -foregroundcolor red
    }
}

The next step is to edit the file.

function processColumnDefaultsXML{
	param (
			[Parameter(Mandatory=$true,Position=1)]
			[object]$context,
			[Parameter(Mandatory=$true,Position=2)]
			[string]$custName,
			[Parameter(Mandatory=$true,Position=3)]
			[string]$listTitle
		)
	
	try{
		[xml]$metadata = Get-Content "client_LocationBasedDefaults.html"
		$MetadataDefaults = $metadata.SelectSingleNode('//MetadataDefaults')
		 
		$MMS = [Microsoft.SharePoint.Client.Taxonomy.TaxonomySession]::GetTaxonomySession($context)
		$context.Load($MMS)
		$context.ExecuteQuery()
		 
		$TermStores = $MMS.TermStores
		$context.Load($TermStores)
		$context.ExecuteQuery()
		 
		$TermStore = $TermStores[0]
		$context.Load($TermStore)
		$context.ExecuteQuery()
		 
		$termGroup = $TermStore.Groups.GetByName("[term group]");
		$termSet = $termGroup.TermSets.GetByName("[termset]");
		 
		$Terms = $TermSet.Terms
		$context.Load($Terms)
		 
		$context.ExecuteQuery()
		 
		Foreach ($Term in $Terms)
		{
			if($Term.Name -eq [if term equals something]){
				$folderPath = "/sites/[sitename]/[list title] #/[optional folders]
                                # for some reason the folder path is case sensitive
				$termString = "-1;#"+$term.Name+"|"+$Term.Id
							 
				$child = $metadata.CreateElement("a")
				$child.SetAttribute("href",[String]$folderPath)
				$subchild = $metadata.CreateElement("DefaultValue")
				$subchild.SetAttribute("FieldName","Customer")
				$subchild.InnerText = [String]$termString
				$child.AppendChild($subchild);
				$MetadataDefaults.AppendChild($child)
			}
		}
		 
		$metadata.Save("{0}\\client_LocationBasedDefaults.html" -f (get-location))
		
	    write-host "info: Process of locationbaseddefaults successful" -foregroundcolor green
    }
    catch{
        write-host "info: $($_.Exception.Message)" -foregroundcolor red
    }
}

After editing the file, we need to upload it again to the SharePoint library.

function uploadColumnDefaultsXML{
	param (
			[Parameter(Mandatory=$true,Position=1)]
			[object]$context,
			[Parameter(Mandatory=$true,Position=2)]
			[object]$list
		)
	
	try{
		$context.Load($list.RootFolder.Folders);
		$context.ExecuteQuery()
		
		$folder = $list.RootFolder.Folders | where { $_.Name -eq "Forms" }
		$fci = New-Object Microsoft.SharePoint.Client.FileCreationInformation
		$fci.Content = [System.IO.File]::ReadAllBytes("[full path to file]\client_LocationBasedDefaults.html");
		$fci.Url = "client_LocationBasedDefaults.html";
		$fci.Overwrite = $true;
		$fileToUpload = $folder.Files.Add($fci);
		$context.Load($fileToUpload);
		$context.ExecuteQuery()    
		
		write-host "info: Upload of locationbaseddefaults successful" -foregroundcolor green
    }
    catch{
        write-host "info: $($_.Exception.Message)" -foregroundcolor red
    }
}

If the file did not exist yet, we need to attach the event receiver as well.

function addEventReceiver{
	param (
			[Parameter(Mandatory=$true,Position=1)]
			[object]$context,
			[Parameter(Mandatory=$true,Position=2)]
			[object]$list
		)
	
	try{
		$eventReceiverName = "LocationBasedMetadataDefaultsReceiver ItemAdded";
        
        $newEventReceiver = New-Object Microsoft.SharePoint.Client.EventReceiverDefinitionCreationInformation
        $newEventReceiver.EventType = [Microsoft.SharePoint.Client.EventReceiverType]::ItemAdded
        $newEventReceiver.Synchronization = [Microsoft.SharePoint.Client.EventReceiverSynchronization]::Synchronous
        $newEventReceiver.ReceiverAssembly = "Microsoft.Office.DocumentManagement, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c";
        $newEventReceiver.ReceiverClass = "Microsoft.Office.DocumentManagement.LocationBasedMetadataDefaultsReceiver";
        $newEventReceiver.ReceiverName = $eventReceiverName;
        $newEventReceiver.SequenceNumber = 1000;
        $list.EventReceivers.Add($newEventReceiver);
            
		$context.Load($list);
        $context.ExecuteQuery();
	
	    write-host "info: Adding eventreceiver successful" -foregroundcolor green
    }
    catch{
        write-host "info: $($_.Exception.Message)" -foregroundcolor red
    }
}

With some additional Powershell code you can iterate over sites and libraries and then call the functions above to make the magic happen.

That’s it!