{"id":1384,"date":"2012-09-03T12:43:09","date_gmt":"2012-09-03T12:43:09","guid":{"rendered":"https:\/\/poiseddevelopers.com\/reality-tech\/?p=1384"},"modified":"2024-05-07T07:08:09","modified_gmt":"2024-05-07T07:08:09","slug":"automating-creation-of-per-location-views","status":"publish","type":"post","link":"https:\/\/poiseddevelopers.com\/reality-tech\/automating-creation-of-per-location-views\/","title":{"rendered":"Automating Creation of Per-Location Views"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_65 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title \" >Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 eztoc-toggle-hide-by-default' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/poiseddevelopers.com\/reality-tech\/automating-creation-of-per-location-views\/#Automating_the_Creation_of_Per-Location_Views\" title=\"Automating the Creation of Per-Location Views\">Automating the Creation of Per-Location Views<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/poiseddevelopers.com\/reality-tech\/automating-creation-of-per-location-views\/#Configuring_Per-Location_Views_Manually\" title=\"Configuring Per-Location Views Manually\">Configuring Per-Location Views Manually<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/poiseddevelopers.com\/reality-tech\/automating-creation-of-per-location-views\/#How_SharePoint_Stores_Per-Location_Views\" title=\"How SharePoint Stores Per-Location Views\">How SharePoint Stores Per-Location Views<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/poiseddevelopers.com\/reality-tech\/automating-creation-of-per-location-views\/#The_approach\" title=\"The approach\">The approach<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/poiseddevelopers.com\/reality-tech\/automating-creation-of-per-location-views\/#Step-by-Step_programmatically_adding_a_Per-Location_View\" title=\"Step-by-Step, programmatically adding a Per-Location View\">Step-by-Step, programmatically adding a Per-Location View<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Automating_the_Creation_of_Per-Location_Views\"><\/span>Automating the Creation of Per-Location Views<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>SharePoint offers the capability of customizing the Views in Folders.\u00a0 This can be done manually.\u00a0 This article will show you how to automate the configuration of views for each folder in a document library.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Configuring_Per-Location_Views_Manually\"><\/span>Configuring Per-Location Views Manually<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>To configure Per-Location Views manually, one must have at least library designer privileges. First, go into Library Settings, then select Per-Location Views:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1406 size-full aligncenter\" src=\"https:\/\/poiseddevelopers.com\/reality-tech\/wp-content\/uploads\/2024\/03\/perloc1.webp\" alt=\"img\" width=\"659\" height=\"500\" srcset=\"https:\/\/poiseddevelopers.com\/reality-tech\/wp-content\/uploads\/2024\/03\/perloc1.webp 659w, https:\/\/poiseddevelopers.com\/reality-tech\/wp-content\/uploads\/2024\/03\/perloc1-300x228.webp 300w\" sizes=\"auto, (max-width: 659px) 100vw, 659px\" \/><\/p>\n<p>For any View configured for the location, it appears as a folder with a green mark, indicating a custom Per-Location view:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-1408 size-full\" src=\"https:\/\/poiseddevelopers.com\/reality-tech\/wp-content\/uploads\/2024\/03\/perloc2.png\" alt=\"Per-Location View\" width=\"673\" height=\"369\" srcset=\"https:\/\/poiseddevelopers.com\/reality-tech\/wp-content\/uploads\/2024\/03\/perloc2.png 673w, https:\/\/poiseddevelopers.com\/reality-tech\/wp-content\/uploads\/2024\/03\/perloc2-300x164.png 300w\" sizes=\"auto, (max-width: 673px) 100vw, 673px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"How_SharePoint_Stores_Per-Location_Views\"><\/span>How SharePoint Stores Per-Location Views<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>SharePoint stores the per-location view information in XML within a specific property (\u201cclient_MOSS_MetadataNavigationSettings\u201d) of the RootFolder object of the list.\u00a0\u00a0 Here\u2019s how to extract this XML and save it to a file for examination:<\/p>\n<pre data-uw-rm-sr=\"\">$RF=$List.RootFolder\r\n\r\n$RF.Properties[\"client_MOSS_MetadataNavigationSettings\"] &gt; \"L:1 before.xml\"<\/pre>\n<p>Let\u2019s look into the XML and see how Views are stored in this special property.\u00a0 First note below that Views are referenced by the View GUID, the relative View URL, as well as an Index.\u00a0 Positive indexes are available Views.\u00a0 Negative Indexes are hidden Views, and the 0 index is the default View:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1419 size-full aligncenter\" src=\"https:\/\/poiseddevelopers.com\/reality-tech\/wp-content\/uploads\/2024\/03\/perloc3-768x213-1.webp\" alt=\"img\" width=\"768\" height=\"213\" srcset=\"https:\/\/poiseddevelopers.com\/reality-tech\/wp-content\/uploads\/2024\/03\/perloc3-768x213-1.webp 768w, https:\/\/poiseddevelopers.com\/reality-tech\/wp-content\/uploads\/2024\/03\/perloc3-768x213-1-300x83.webp 300w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><\/p>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_approach\"><\/span>The approach<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The basic approach is to grab the XML described above, clean it up from any previous customization, and add folder nodes; and for each folder, add Views.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Step-by-Step_programmatically_adding_a_Per-Location_View\"><\/span>Step-by-Step, programmatically adding a Per-Location View<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>First, let\u2019s grab the List, and Folder objects:<\/p>\n<p>&nbsp;<\/p>\n<pre lang=\"php\">  $web=get-spweb http :\/\/SharePoint\/MyWeb $lists=$web.lists\r\n$list=$lists[\"ListName\"]\r\n$Folders=$list.Folders\r\n$ViewSet=\"All Documents,EMail,Claim View,LC View,UW View\"\r\n$ViewArr=$ViewSet.split(\",\")\r\n<\/pre>\n<p>We can always save the XML for reference:<\/p>\n<pre lang=\"php\"> $RF.Properties[\"client_MOSS_MetadataNavigationSettings\"] &gt; \"L:1 before.xml\"<\/pre>\n<p>Now, let\u2019s grab the Views object, the Root Folder object, and the per-location Views cast as XML:<\/p>\n<pre lang=\"php\"> $Views=$list.Views\r\n$RF=$List.RootFolder\r\n$x=$RF.Properties[\"client_MOSS_MetadataNavigationSettings\"]<\/pre>\n<p>Now let\u2019s wipe out any previous per-location Views customizations.\u00a0 We will simply remove all nodes in the Folder<\/p>\n<pre lang=\"php\">\t\r\n$x.MetadataNavigationSettings.NavigationHierarchies.FolderHierarchy.removeall()<\/pre>\n<p>Anytime we want to examine the XML, we can output it to file, using the InnerXML:<\/p>\n<pre lang=\"php\">$x.InnerXml &gt; \"L:1 after.xml\"<\/pre>\n<p data-uw-rm-sr=\"\">Normally, we would grab the\u00a0 XMLElement using $x.MetadataNavigationSettings.NavigationHierarchies.FolderHierarchy, but this returns a string for a null or initialized node.<br role=\"presentation\" data-uw-rm-sr=\"\" \/>So instead, we grab the parent (NavigationHierarchies), then select the underlying single nodes:<\/p>\n<div><\/div>\n<pre lang=\"php\">$NavHierNode = $x.MetadataNavigationSettings.NavigationHierarchies<\/pre>\n<p data-uw-rm-sr=\"\">$ViewSettingsNode = $NavHierNode.SelectSingleNode(\u201cFolderHierarchy\u201d)<\/p>\n<p>I\u2019ve created Folder Content Types, by inheriting from the Folder Content Type.\u00a0 This approach allows Folders to hold useful metadata and be selectable by users on creation.\u00a0 In this case, I use the Folder Content Type to determine the View.\u00a0 You can make the choice instead on the folder URL or metadata.\u00a0 I simply loop through all folders, and select the preferred default folder based on the location:<\/p>\n<pre lang=\"php\">for ($i=0; $i -lt $FolderCount; $i++)\r\n{\r\n $Folder=$Folders[$i]\r\n \r\n switch ($Folder.ContentType.name)\r\n {\r\n  \"Underwriting Folder\"  { $DefaultViewName= \"UW View\"}\r\n  \"Claim Folder\"   { $DefaultViewName= \"Claim View\"}\r\n  \"Policy Folder\"  { $DefaultViewName= \"UW View\"}\r\n  \"Loss Control Folder\" { $DefaultViewName= \"LC View\"}\r\n  default   { $DefaultViewName= \"All Documents\"}\r\n }<\/pre>\n<p>I grab the actual view, but take care to fall back on All Documents if the View is not found:<\/p>\n<pre lang=\"php\"> $View=$Views[$DefaultViewName];\r\n \r\n if ($View -eq $null)\r\n {\r\n  Write-Host \"View ($($DefaultViewName) not found, falling back to All Documents\"\r\n  $DefaultViewName= \"All Documents\"; # logic assumes this is always there\r\n  $View=$Views[$DefaultViewName];\r\n }\r\n\r\nNow, we want to make note of the folder details for use in building the XML.  As you recall there are three aspects to the View of interest: the GUID, ID and relative URL:\r\n[sourcecode language=\"powershell\"]\r\n$FolderGuid=$Folder.UniqueId\r\n \r\n $FolderID= $Folder.id\r\n \r\n $FolderURL= $Folder.Url;<\/pre>\n<p>So let&#8217;s start with the folder entry in the XML, by creating a new folder node, and populating it&#8217;s attributes, and first element, for the default view:<\/p>\n<pre> $NewFolderNode=$X.CreateElement(\"ViewSettings\");\r\n $NewFolderNode.SetAttribute(\"UniqueNodeId\",$FolderGuid);\r\n $NewFolderNode.SetAttribute(\"FolderId\",$FolderID);\r\n \r\n $NewViewNode=$X.CreateElement(\"View\");\r\n $NewViewNode.SetAttribute(\"ViewId\",$View.id);\r\n $NewViewNode.SetAttribute(\"CachedName\",$View.Title);\r\n $NewViewNode.SetAttribute(\"Index\",\"0\");  #0 forces view to be default\r\n $NewViewNode.SetAttribute(\"CachedUrl\",$View.Url);\r\n $NewFolderNode.AppendChild($NewViewNode);<\/pre>\n<p>Finally, we can add all the other Views for the folder. Note we start the index from 1, after the 0 default view. Now&#8217;s where the array of Views created earlier comes into use. I always like starting with a comma separated list, converting these into an array for easy use. We make sure not to add the default View a second time:<\/p>\n<pre> $Index=1; # we want in increment index for each view for sequence\r\n foreach ($ViewName in $ViewArr)\r\n {\r\n  $View=$Views[$DefaultViewName];\r\n  if ($View -eq $null)\r\n  {\r\n   Write-Host \"View ($($DefaultViewName) not found, secondary view, skipping\" #never do a continue within foreach!\r\n  }\r\n  elseif ($ViewName -ne $DefaultViewName) #make sure to skip adding the default view as a secondary view\r\n  {\r\n   $View=$Views[$ViewName];\r\n \r\n   $NewViewNode=$X.CreateElement(\"View\");\r\n   $NewViewNode.SetAttribute(\"ViewId\",$View.id);\r\n   $NewViewNode.SetAttribute(\"CachedName\",$View.Title);\r\n   $NewViewNode.SetAttribute(\"Index\",$Index.tostring());  #0 forces view to be default\r\n   $NewViewNode.SetAttribute(\"CachedUrl\",$View.Url);\r\n   $NewFolderNode.AppendChild($NewViewNode);\r\n   $Index++; #view sequence numbering\r\n  }\r\n }\r\n<\/pre>\n<p>$ViewSettingsNode.AppendChild($NewFolderNode)<br \/>\nFinally, we save our XML.\u00a0 Note both update() are required:<\/p>\n<pre lang=\"php\">$RF.Properties[\"client_MOSS_MetadataNavigationSettings\"]=$x.InnerXml.ToString();\r\n$RF.Update()\r\n$list.Update() #both property and List update are required<\/pre>\n<p>[\/sourcecode ]<\/p>\n<p>See: <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/microsoft.office.documentmanagement.metadatanavigation.metadatanavigationsettings.aspx\" target=\"_blank\" rel=\"noopener\">Technet reference<\/a><br \/>\nLet&#8217;s finally put it all together:<br \/>\n[sourcecode language=&#8221;powershell&#8221;]<br \/>\n$web=get-spweb href=&#8221;http :\/\/SharePoint\/MySite&#8221;<br \/>\n$lists=$web.lists<br \/>\n$list=$lists[&#8220;MyListName&#8221;]<br \/>\n$Folders=$list.Folders<\/p>\n<p>$ViewSet=&#8221;All Documents,EMail,Claim View,LC View,UW View&#8221;<br \/>\n$ViewArr=$ViewSet.split(&#8220;,&#8221;)<\/p>\n<p>$WipeFolderDefaults=$true; # This is optional<\/p>\n<p>$Views=$list.Views<br \/>\n$RF=$List.RootFolder<br \/>\n#$x.get_Properties()<br \/>\n[xml][\/xml] $x=$RF.Properties[&#8220;client_MOSS_MetadataNavigationSettings&#8221;]<\/p>\n<p>if ($WipeFolderDefaults)<br \/>\n{<br \/>\ntry #if it fails, it is because the node just isn&#8217;t there, which might mean no folders defined, which is fine.<br \/>\n{<br \/>\n$x.MetadataNavigationSettings.NavigationHierarchies.FolderHierarchy.removeall()<br \/>\n}<br \/>\ncatch<br \/>\n{<br \/>\nwrite-host &#8220;nothing to wipe, we are good to go!&#8221;<br \/>\n}<br \/>\n}<\/p>\n<p>$FolderCount=$Folders.count;<br \/>\n$NavHierNode = $x.MetadataNavigationSettings.NavigationHierarchies<br \/>\n$ViewSettingsNode = $NavHierNode.SelectSingleNode(&#8220;FolderHierarchy&#8221;) #grabs it as XMLNode, instead of string, if empty node<br \/>\nfor ($i=0; $i -lt $FolderCount; $i++)<br \/>\n{<br \/>\n$Folder=$Folders[$i]<br \/>\nswitch ($Folder.ContentType.name)<br \/>\n{<br \/>\n&#8220;Underwriting Folder&#8221; { $DefaultViewName= &#8220;UW View&#8221;}<br \/>\n&#8220;Claim Folder&#8221; { $DefaultViewName= &#8220;Claim View&#8221;}<br \/>\n&#8220;Policy Folder&#8221; { $DefaultViewName= &#8220;UW View&#8221;}<br \/>\n&#8220;Loss Control Folder&#8221; { $DefaultViewName= &#8220;LC View&#8221;}<br \/>\ndefault { $DefaultViewName= &#8220;All Documents&#8221;}<br \/>\n}<\/p>\n<p>$View=$Views[$DefaultViewName];<br \/>\nif ($View -eq $null)<br \/>\n{<br \/>\nWrite-Host &#8220;View ($($DefaultViewName) not found, falling back to All Documents&#8221;<br \/>\n$DefaultViewName= &#8220;All Documents&#8221;; # logic assumes this is always there<br \/>\n$View=$Views[$DefaultViewName];<br \/>\n}<\/p>\n<p>$FolderGuid=$Folder.UniqueId<br \/>\n$FolderID= $Folder.id<br \/>\n$FolderURL= $Folder.Url;<\/p>\n<p>$NewFolderNode=$X.CreateElement(&#8220;ViewSettings&#8221;);<br \/>\n$NewFolderNode.SetAttribute(&#8220;UniqueNodeId&#8221;,$FolderGuid);<br \/>\n$NewFolderNode.SetAttribute(&#8220;FolderId&#8221;,$FolderID);<\/p>\n<p>$NewViewNode=$X.CreateElement(&#8220;View&#8221;);<br \/>\n$NewViewNode.SetAttribute(&#8220;ViewId&#8221;,$View.id);<br \/>\n$NewViewNode.SetAttribute(&#8220;CachedName&#8221;,$View.Title);<br \/>\n$NewViewNode.SetAttribute(&#8220;Index&#8221;,&#8221;0&#8243;); #0 forces view to be default<br \/>\n$NewViewNode.SetAttribute(&#8220;CachedUrl&#8221;,$View.Url);<br \/>\n$NewFolderNode.AppendChild($NewViewNode);<br \/>\n$Index=1; # we want in increment index for each view for sequence<br \/>\nforeach ($ViewName in $ViewArr)<br \/>\n{<br \/>\n$View=$Views[$DefaultViewName];<br \/>\nif ($View -eq $null)<br \/>\n{<br \/>\nWrite-Host &#8220;View ($($DefaultViewName) not found, secondary view, skipping&#8221; #never do a continue within foreach!<br \/>\n}<br \/>\nelseif ($ViewName -ne $DefaultViewName) #make sure to skip adding the default view as a secondary view<br \/>\n{<br \/>\n$View=$Views[$ViewName];<\/p>\n<p>$NewViewNode=$X.CreateElement(&#8220;View&#8221;);<br \/>\n$NewViewNode.SetAttribute(&#8220;ViewId&#8221;,$View.id);<br \/>\n$NewViewNode.SetAttribute(&#8220;CachedName&#8221;,$View.Title);<br \/>\n$NewViewNode.SetAttribute(&#8220;Index&#8221;,$Index.tostring()); #0 forces view to be default<br \/>\n$NewViewNode.SetAttribute(&#8220;CachedUrl&#8221;,$View.Url);<br \/>\n$NewFolderNode.AppendChild($NewViewNode);<br \/>\n$Index++; #view sequence numbering<br \/>\n}<br \/>\n}<\/p>\n<p>$ViewSettingsNode.AppendChild($NewFolderNode)<\/p>\n<p>}<\/p>\n<p>#$x.InnerXml &gt; &#8220;L:PowerShellPer Location Views2a.xml&#8221;<\/p>\n<p>$RF.Properties[&#8220;client_MOSS_MetadataNavigationSettings&#8221;]=$x.InnerXml.ToString();<br \/>\n$RF.Update()<br \/>\n$list.Update() #both property and List update are required<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Automating the Creation of Per-Location Views SharePoint offers the capability of customizing the Views in Folders.\u00a0 This can be done manually.\u00a0 This article will show you how to automate the configuration of views for each folder in a document library. Configuring Per-Location Views Manually To configure Per-Location Views manually, one must have at least library [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":1386,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[26],"tags":[],"class_list":["post-1384","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell"],"acf":[],"_links":{"self":[{"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/posts\/1384","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/comments?post=1384"}],"version-history":[{"count":8,"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/posts\/1384\/revisions"}],"predecessor-version":[{"id":3770,"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/posts\/1384\/revisions\/3770"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/media\/1386"}],"wp:attachment":[{"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/media?parent=1384"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/categories?post=1384"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/poiseddevelopers.com\/reality-tech\/wp-json\/wp\/v2\/tags?post=1384"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}