Albert Jan Schot
 

Reusing SharePoint Designer Workflow templates within Visual Studio

05

Apr

Just a few quickies in case you want to export a SharePoint Designer Workflow straight into Visual Studio there are a few things that might save you quite some time. In this blog I try to cover the things I encounter.


First of all, if you want to import your globally reusable workflow using the Import Reusable Workflow you need to make sure it is published. If your workflow is not published before it’s saved as a template it will not be recognized in the import wizard, thus making it impossible to import it.
When creating the workflow with the SharePoint designer it is pretty easy to give a fancy name to the workflow, in my case it was called Provisioning Approval Workflow, saving it as a template doesn’t throw any error, then importing seems to work, however as soon as you try to package the new project it starts throwing errors like:
Activity 'RootWorkflowActivityWithData' validation failed: Property 'Name' has invalid value. The identifier  '' is not a valid name.   
So the x:Name attribute is not supporting a space in the name, that’s an easy fix, by changing it, however after doing so the assembly building fails because it tried to make a class called: 
public partial class Provisioning Approval Workflow


You can start fixing those issues one by one, but also just before publishing and exporting the workflow replace any spaces in the name for _ so everything will compile right away.
Now there is one more minor thing before the project actually builds, it is missing references.

If you have custom workflow activities that might be expected behavior (just add them), but by default it is also missing the Microsoft.SharePoint.WorkflowActions. That one can be found on the location: C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.WorkflowActions.dll
and after adding that your imported workflow will most probably compile.

Once it compiles you might want to change the feature name because it still has a default Feature1 name. Also you might want to check the Elements.xml file in the Workflow object for the Name and Description (in the Name you can strip out any _ and return it as a fancy name. Also you can probably strip out the whole <MetaData> block since that contains almost only empty elements (except for the StatusPageUrl that, if you don’t roll-out a custom StatusPageUrl can be left blank to get the default one). Also you can strip out the InstantiationUrl , ModificationUrl and StatusUrl since they will throw errors when executing the workflow (throwing an error like: The workflow template has specified no FormURN for this page.). If you don’t throw out the MetaData block make sure to delete the AssociationCategories node because otherwise provisioning the Workflow to a ContentType.

Share:

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot

Start crawling with PowerShell part 2

05

Oct

A while ago I blogged about starting a new crawl with PowerShell in this post, but today I got a bit further, each rollout I do on my dev machine a whole new site collection gets created using PowerShell, after that there is some test content provisioned (also using PowerShell), and finally the search settings are added. It kinda feels like everything is done through PowerShell. The point however is that I not only want to start a new crawl, but make sure that ‘old’ content is deleted. Step by step; I create some Crawled properties, create some Managed Metadata properties, create some scopes and finally delete the old content and crawl the new one.

The first part creating crawled properties and metadata properties i used this blog post. Getting me something like the following, that checks if the crawled property exists and if so gets it, otherwise creates it, then setting it into a metadata property that can be used. Nice to know is that the –propset property even though the documentation says its optional is in fact required!

  1: # Get or create SPEnterpriseSearchMetadataCrawledProperty 
  2: if (Get-SPEnterpriseSearchMetadataCrawledProperty 
  3:         -SearchApplication $searchapp -Name "proptocreate" 
  4:         -ea "silentlycontinue") {
  5:   $crawlprop = Get-SPEnterpriseSearchMetadataCrawledProperty 
  6:           -SearchApplication $searchapp -Name "proptocreate" 
  7: } else {
  8:   $crawlprop = New-SPEnterpriseSearchMetadataCrawledProperty 
  9:           -SearchApplication $searchapp -VariantType 31 
 10:           -Name proptocreate -IsNameEnum $false -PropSet "00130329-0000-0130-c000-000000131346"
 11: }
 12: 
 13: if (Get-SPEnterpriseSearchMetadataManagedProperty 
 14:         -SearchApplication $searchapp -Identity "MetadataPorpertyExample" 
 15:         -ea "silentlycontinue") {
 16:    write-host -f Green "MetaProperty already exists, 
 17:           we delete it so it can be reacreated "
 18:    $prop = Get-SPEnterpriseSearchMetadataManagedProperty 
 19:           -SearchApplication $searchapp -Identity "PublishingPageContent"
 20:    $prop.DeleteAllMappings()
 21:    $prop.Delete()
 22:    $searchapp.Update()
 23: } else {}
 24: 
 25: write-host -f Green "Try to create MetaProperty"
 26: $prop = New-SPEnterpriseSearchMetadataManagedProperty
 27:    -SearchApplication $searchapp -Name "PublishingPageContent" -Type 1
 28: $prop.EnabledForScoping = $true
 29: $prop.Update()
 30: 
 31: New-SPEnterpriseSearchMetadataMapping -SearchApplication 
 32: $searchapp -ManagedProperty $prop -CrawledProperty $crawlprop

 

After that i can create scopes (my colleague Peter found it out), where I check if a scope already exists and if so delete it so it can be recreated again:

  1: if (Get-SPEnterpriseSearchQueryScope -SearchApplication $searchapp 
  2:         -Identity "ScopeExample" -ea "silentlycontinue") {
  3:   write-host -f Green "Scope already exists, 
  4:            we delete it so it can be reacreated "
  5:   $scope= Get-SPEnterpriseSearchQueryScope -SearchApplication 
  6:             $searchapp -Identity "ScopeExample" 
  7:   $scope.Delete();
  8:   $searchapp.Update();
  9: } else {}
 10: 
 11: # Create "ScopeExample" scope
 12: $scope = New-SPEnterpriseSearchQueryScope -Name "ScopeExample" 
 13:    -Description "Doorzoek alle informatie" -SearchApplication $searchapp 
 14:    -DisplayInAdminUI $true
 15: 
 16: New-SPEnterpriseSearchQueryScopeRule -RuleType AllContent 
 17:   -Url $url -scope $scope

 

And after that in finally can reset my index, and make sure everything is found:

  1: Write-Host -f Green "Delete current index ...";
  2: $searchapp.Reset($true, $true)
  3: 
  4: Write-Host -f Green "Start indexing and scopecompilation ";
  5: $searchapp.StartScopesCompilation()
  6: 
  7: $CrawlContents = Get-SPEnterpriseSearchServiceApplication 
  8:     | Get-SPEnterpriseSearchCrawlContentSource
  9: 
 10: foreach ($crawlcontent in $CrawlContents) 
 11:     { $crawlcontent.StartFullCrawl(); }

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot

DIWUG SharePoint Magazine

29

Sep

The 3th edition of the free DIWUG SharePoint magazine is out, you can download it here, this time there is an article about PowerShell and SharePoint in there, and its all written by me. Pretty proud on that one.

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot

Deploying SPD Workflows with PowerShell

27

Sep

With the new SharePoint Designer creating reusable workflows has become fairly easy, however deploying them seems to be a bit harder, lets say you create a reusable workflow that you use globally  on all your content types, it seems it still has to be deployed to each list you use it in. So saying you would have 50 lists with that content type forces you to add that workflow 50 times.

For that reason the following PowerShell example might come in handy, it allows you to search a workflow you create with SharePoint designer, and add that to your SPList (ofcourse you can also use an SPWeb or other object that supports the SPWorkflowAssociation to bind it to).

 

  1: #Clear old output
  2: clear; 
  3: 
  4: #Write some header
  5: Write-Host -ForegroundColor Green "Publish Workflow Test"; 
  6: 
  7: #Load required dll
  8: [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Workflow") 
  9: 
 10: #Load Snappin 
 11: $snapin="Microsoft.SharePoint.PowerShell"
 12: if (get-pssnapin $snapin -ea "silentlycontinue") {}
 13: else {
 14:       if (get-pssnapin $snapin -registered -ea "silentlycontinue") {
 15:             Add-PSSnapin $snapin
 16:             write-host -f Green "PSsnapin $snapin is loaded"
 17:       }
 18:       else {
 19:             write-host -f Red "PSSnapin $snapin not found" 
 20:       }
 21: }
 22: 
 23: #Get some required objects
 24: $web = Get-SPWeb -Identity "http://portal.sp2010.dev";
 25: $rootweb = $web.Site.RootWeb; 
 26: $wfList = $web.Lists["Pages"];
 27: $wfTaskList = $web.Lists["Workflow Tasks"];
 28: $wfHistoryList = $web.Lists["Workflow Tasks"];
 29: 
 30: #Get Workflow
 31: foreach($workflow in $rootweb.WorkflowTemplates) 
 32: {
 33:       #User $workflow.Name to mach on 
 34:       Write-Host -ForegroundColor Yellow " Found a workflow: " $workflow.Name 
 35:       
 36:       if($workflow.Name -eq "TestAppie") 
 37:       {
 38:             $setflow = $workflow; 
 39:       }
 40: }
 41: 
 42: Write-Host -ForegroundColor Green "Found the workflow: " $setflow.Name; 
 43: 
 44: $association = [Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::CreateListContentTypeAssociation($setflow, "Test", $wfTask, $wfHistory);
 45: 
 46: $list.WorkflowAssociations.Add($association); 
 47: $list.Update();
 48: 

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot

Editing pages with the SharePoint Designer 2010

24

Sep

So always nice to update a few minor things on a pagelayout using SharePoint Designer, especially if it has to be done quick, however this week I spend the first hour cursing why SPD wouldn't allow me to edit my pagelayout. Seems to be that opening it, checking it out isn’t enough. You have to press the advanced button…

Opening a pagelayout took a few minutes to get, due to the fact that in SPD2010 you have to click the ‘edit file’ after opening the file. Once you are in editing mode all the html in code view is a bit yellow though, and not editable. To get it editable you have to click the Advanced mode button, after doing so you will be able to edit your page as like. However once you clicked it, it will grey out..

 

image

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot

Updating Custom Ribbon Actions

22

Sep

Last few days I saw quite a few forum posts about custom ribbon actions that where created but where not working as expected, or not updated the second time they got deployed (see this and this post).

Now assuming you have the same issue; there are two things you need to check:

1) Make sure that you deleted your IE (or other browsers) cache after deploying, since the ribbon caches quite a lot of stuff.

2) When creating a feature that contains the xml / js for your custom ribbon action you need to make sure that you add a version to your feature tag. This version needs to be updated every time you roll-out a new version of your ribbon action.

Adding a version to a feature is fairly simple: if you open the desired feature you will see a 'button' on the bottom left called manifest, if you click that you have an option edit options around the same place if you click that you can edit the feature xml by default it will be:

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/">
</Feature>

So within that <feature> tag you can add a version="1.0.0.0", so it would look like:

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/" version="1.0.0.0">
</Feature>

Next time you changed something and want to rollout again you can use version="1.0.0.1" or higher to make sure all files get updated.

Share:

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot

Adding a noteboard to a pagelayout

21

Sep

Adding the new note board to your pagelayouts seems like a nice way to allow comments on newsitems and provide a some more social element within your site, however in terms of configuration, you don't want to have to add the note board to each page you created, and add it to a pagelayout. So I created some pagelayouts and  wanted to add the note board WebPart to it using its xml, therefore I exported the Note Board WebPart, however when putting it back on the page it gave me an error, so once exported it can not be imported again.

If you want to add the WebPart to your pagelayout you can use the following xml to do so (it is taken from the MySiteHost Site Defintion, and works as a charm):

 

  1: <WebPart xmlns="http://schemas.microsoft.com/WebPart/v2">
  2:   <Assembly>Microsoft.SharePoint.Portal, Version=14.0.0.0, 
  3:            Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
  4:   <TypeName>Microsoft.SharePoint.Portal.WebControls.
  5:            SocialCommentWebPart</TypeName>
  6:   <Title>Note board</Title>
  7:   <Description>Description</Description>
  8:   <PartOrder>3</PartOrder>
  9:   <FrameType>TitleBarOnly</FrameType>
 10:   <AllowMinimize>true</AllowMinimize>
 11:   <AllowRemove>false</AllowRemove>
 12:   <IsVisible>true</IsVisible>
 13: </WebPart>
Share:

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot

Batch Publish with the Ribbon

17

Aug

After creating my first ribbon control (blogged about that in this post), I wanted to learn a bit more about the client object model in SharePoint 2010. So due to that I decided to make a new ribbon button that would allow me to publish multiple items, using the Client Object model (allowing you to use the solution as a sandboxed solution.

I used almost the same code as in this post, only adding two new things; a custom action containing the script that would actually publish items, and some code so the button would only be enabled whenever there are one or more items selected:

  1:   <CustomAction Id="Ribbon.Documents.Custom.Scripts"
  2:       Location ="ScriptLink"
  3:       ScriptSrc="/_layouts/Portal/Portal.RibbonActions.js" />

 

and: 

  1: <CommandUIHandler
  2:  Command="PublishForMail_PublishButton"
  3:  CommandAction="javascript:PublishDocuments();"
  4:  EnabledScript="javascript:function oneOrMoreEnable() { 
  5:   var items = SP.ListOperation.Selection.getSelectedItems(); 
  6:   var ci = CountDictionary(items); 
  7:   return (ci > 0); 
  8:  } 
  9:  oneOrMoreEnable();" />

 

So the complete code for the ribbon looked like:

  1: <?xml version="1.0" encoding="utf-8"?>
  2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  3:   <CustomAction
  4:     Id="Ribbon.Documents.Custom.BatchPublish"
  5:     Location="CommandUI.Ribbon"
  6:     RegistrationType="ContentType"
  7:     RegistrationId="0x01">
  8:     <CommandUIExtension>
  9:       <CommandUIDefinitions>
 10:         <CommandUIDefinition
 11:           Location="Ribbon.Documents.Groups._children">
 12:           <Group
 13:             Id="Ribbon.Documents.Custom"
 14:             Sequence="100"
 15:             Description="Custom Controls"
 16:             Title="Custom Controls"
 17:             Template="Ribbon.Documents.Custom.CustomTemplate">
 18:             <Controls Id="Ribbon.Documents.Custom.Controls">
 19:               <Button
 20:                 Id="Ribbon.Documents.Custom.Controls.BatchPublish"
 21:                 Sequence="5"
 22:                 Command="BatchPublish_PublishButton"
 23:                 Image16by16="/_layouts/images/Portal/publish16.png"
 24:                 Image32by32="/_layouts/images/Portal/publish32.png"
 25:                 ToolTipTitle="Release Selected Documents"
 26:                 ToolTipDescription="Publish a major version of all selected documents"
 27:                 LabelText="Release Selection"
 28:                 TemplateAlias="o1" />
 29:             </Controls>
 30:           </Group>
 31:         </CommandUIDefinition>
 32:         
 33:         <CommandUIDefinition
 34:             Location="Ribbon.Templates._children">
 35:           <GroupTemplate Id="Ribbon.Documents.Custom.CustomTemplate">
 36:             <Layout Title="LargeLarge">
 37:               <OverflowSection Type="OneRow" TemplateAlias="o1" DisplayMode="Large"/>
 38:               <OverflowSection Type="OneRow" TemplateAlias="o2" DisplayMode="Large"/>
 39:             </Layout>
 40:             <Layout Title="LargeMedium">
 41:               <OverflowSection Type="OneRow" TemplateAlias="o1" DisplayMode="Large"/>
 42:               <OverflowSection Type="ThreeRow" TemplateAlias="o2" DisplayMode="Medium"/>
 43:             </Layout>
 44:             <Layout Title="LargeSmall">
 45:               <OverflowSection Type="OneRow" TemplateAlias="o1" DisplayMode="Large"/>
 46:               <OverflowSection Type="ThreeRow" TemplateAlias="o2" DisplayMode="Small"/>
 47:             </Layout>
 48:             <Layout Title="MediumLarge">
 49:               <OverflowSection Type="ThreeRow" TemplateAlias="o1" DisplayMode="Medium"/>
 50:               <OverflowSection Type="OneRow" TemplateAlias="o2" DisplayMode="Large"/>
 51:             </Layout>
 52:             <Layout Title="MediumMedium">
 53:               <OverflowSection Type="ThreeRow" TemplateAlias="o1" DisplayMode="Medium"/>
 54:               <OverflowSection Type="ThreeRow" TemplateAlias="o2" DisplayMode="Medium"/>
 55:             </Layout>
 56:             <Layout Title="MediumSmall">
 57:               <OverflowSection Type="ThreeRow" TemplateAlias="o1" DisplayMode="Medium"/>
 58:               <OverflowSection Type="ThreeRow" TemplateAlias="o2" DisplayMode="Small"/>
 59:             </Layout>
 60:             <Layout Title="SmallLarge">
 61:               <OverflowSection Type="ThreeRow" TemplateAlias="o1" DisplayMode="Small"/>
 62:               <OverflowSection Type="OneRow" TemplateAlias="o2" DisplayMode="Large"/>
 63:             </Layout>
 64:             <Layout Title="SmallMedium">
 65:               <OverflowSection Type="ThreeRow" TemplateAlias="o1" DisplayMode="Small"/>
 66:               <OverflowSection Type="ThreeRow" TemplateAlias="o2" DisplayMode="Medium"/>
 67:             </Layout>
 68:             <Layout Title="SmallSmall">
 69:               <OverflowSection Type="ThreeRow" TemplateAlias="o1" DisplayMode="Small"/>
 70:               <OverflowSection Type="ThreeRow" TemplateAlias="o2" DisplayMode="Small"/>
 71:             </Layout>
 72:             <Layout Title="Popup" LayoutTitle="LargeLarge" />
 73:           </GroupTemplate>
 74:         </CommandUIDefinition>
 75: 
 76:         <CommandUIDefinition
 77:             Location="Ribbon.Documents.Scaling._children">
 78:           <MaxSize
 79:             Id="Ribbon.Documents.Scaling.Custom.MaxSize"
 80:             Sequence="15"
 81:             GroupId="Ribbon.Documents.Custom"
 82:             Size="LargeLarge" />
 83:         </CommandUIDefinition>
 84:       </CommandUIDefinitions>
 85:       
 86:       <CommandUIHandlers>
 87:         
 88:         <CommandUIHandler
 89:            Command="BatchPublish_PublishButton"
 90:            CommandAction="javascript:PublishDocuments();"
 91:            EnabledScript="javascript:function oneOrMoreEnable() { 
 92:                           var items = SP.ListOperation.Selection.getSelectedItems(); 
 93:                           var ci = CountDictionary(items); 
 94:                             return (ci > 0); 
 95:                           } 
 96:                           oneOrMoreEnable();" />
 97:       </CommandUIHandlers>
 98:     </CommandUIExtension>
 99:   </CustomAction>
100: 
101:   <CustomAction Id="Ribbon.Documents.Custom.Scripts"
102:       Location ="ScriptLink"
103:       ScriptSrc="/_layouts/Portal/Portal.RibbonActions.js" />
104: 
105: </Elements>

 

And as you could see we would call a function PublishDocuments(), so withing the Portal.RibbonActions.js we would need to have that function. From the context we can get the selected items, and the GUID of the current list, that allows you to create a SPList retrieving the SPListItems based on their ID and do the things you want, finally allowing you to check if actions succeeded and if not make a mention of it.

  1: PublishDocuments = function () {
  2:     var selecteditems = SP.ListOperation.Selection.getSelectedItems();
  3:     var currentListGuid = SP.ListOperation.Selection.getSelectedList();
  4: 
  5:     var context = SP.ClientContext.get_current();
  6:     var currentWeb = context.get_web();
  7:     var currentList = currentWeb.get_lists().getById(currentListGuid)
  8: 
  9:     var k;
 10: 
 11:     for (k in selecteditems) {
 12:         var listitem = currentList.getItemById(selecteditems[k].id);
 13:         var spfile = listitem.get_file();
 14: 
 15:         spfile.publish("Publish, so file can be send to client");            
 16:     }
 17:     
 18:     context.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));
 19: }
 20: 
 21: function success() {
 22:     SP.UI.Notify.addNotification('Succesfully published major versions'); 
 23: }
 24: function failed(sender, args) {
 25:     var statusId = SP.UI.Status.addStatus(args.get_message());
 26:     SP.UI.Status.setStatusPriColor(statusId, 'red'); 
 27:     latestId = statusId;
 28: }

 

As you can see we try a spfile.publish(), and then check if it was a success, and use the Notify option to show to a user that it was a success, if it failed we use the Status option that shows a red bar with the error.

 

As you can see with around 30 lines of JavaScript you can batch publish your files.

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot

SharePoint 2007 to 2010 upgrade issue with navigation

03

Aug

Upgrading a SharePoint 2007 to a SharePoint 2010 environment and all options you have is something that is handled quite well on the web, personally i love this post on how to do it, but there are several other posts out there making it pretty easy. However I ran into a ‘small’ bug upgrading.

I have a SharePoint farm containing some info, and a Dutch language pack, thus I recreated the same environment as a SharePoint 2010 containing a Dutch language pack, and using a database upgrade. And as you can see in the picture below, there are no errors, and adding the content DB went well as expected.

addcontentdb 

However, the results when looking at in the browser where kind of sad:

Foutmelding menu

After a long search on the web someone mentioned turning off and on the Publication Infrastructure for SharePoint, turning it of resolved the issue, but turning it on again instantly returned it. So I found myself struggling with the issue for another day not being able to find the exact bug, but nice fix.

Apparently the Menu control expects a /paginas library that contains the pages, but it isn’t there, so it throws an error, since I didn't want to change my farm to much I created a small PowerShell scripts that would fix the error.

The result is a working site:

Werkend menu

Script 1 – Creating Libraries 

  1: [Void][System.Reflection.Assembly]::LoadWithPartialName
  2:   ("Microsoft.SharePoint")
  3: [Void][System.Reflection.Assembly]::LoadWithPartialName
  4:   ("Microsoft.SharePoint.Publishing")
  5: 
  6: function CheckaWeb($rootweb)
  7: {
  8:   #Step 1 Check For Lists
  9:   $list = $rootweb.Lists[$from];
 10:   if($list -ne $null) {createList $rootweb $list}
 11:   
 12:   #Check for subwebs
 13:   if($rootweb.Webs -ne $null)
 14:   {
 15:     # Recursive Call To SubWeb
 16:     foreach($web in $rootweb.Webs)
 17:     {
 18:        CheckAWeb $web
 19:     }
 20:   }
 21: }
 22: 
 23: 
 24: function createList ($web, $list)
 25: {
 26:   Write-Host -ForegroundColor Green 
 27:           " Found a web containing a PageList on url" $web.Url 
 28:   
 29:   $web.Lists.Add("Paginas", 
 30:             "Automatisch gegenereerde bibliotheek voor 
 31:              SharePoint 2010 upgrade", 101)
 32: }
 33: 
 34: 
 35: Write-Host ""
 36: Write-Host -ForegroundColor Green 
 37:     "Fix Dutch SharePoint 2010 Upgrade Script v1.0 - Albert-Jan Schot "
 38: Write-Host -ForegroundColor Green 
 39:     "contact appie@tamtam.nl"
 40: Write-Host ""
 41: 
 42: $site=new-object Microsoft.SharePoint.SPSite('http://restore.sp2010.dev')
 43: $from = "Pagina's"
 44: 
 45: Write-Host -f Blue " "
 46: 
 47: CheckaWeb $site.Rootweb $true  
 48: 
 49: Write-Host " "
 50: Write-Host -f Blue "Done checkin webs"
 51: Write-Host " "

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot

Moving a DocumentSet through code - pt1

20

Jul

One of the new features in SharePoint 2010 are the so called DocumentSets, a sort of folders-like option that allows you to manage multiple documents as they where a set, setting global metadata or capturing versions and downloading multiple documents. Since those options sounds pretty cool I recon it’s going to be a much used option within SharePoint 2010, however once you created a DocumentSet moving it around can be pretty hard, by default there are a few ways to move around your environment:

  • You can move items around in the Explorer view of your libraries, and doing so with a DocumentSet results in the copying of a folder, losing all metadata for your DocumentSet (and as you might have noticed once you set the ContentType back to DocumentSet it still shows as a folder).
  • On the other hand you can use the “send-to” option you get, but that will results in the sending of a ZIP package, still losing the information as it seems. 
  • The only way by default moving around works, is by using the Site Content and Structure.

So we decided to check if we could manage it through code. In this first part there will be some PowerShell examples on how to move a DocumentSet and where you might find yourself trying to do so, in the next part there will be some focus on how to move the DocumentSet through the ribbon, including all the metadata of it.

According to TechNet a DocumentSet is a special type of folder, and checking out MSDN shows that the the DocumentSet object does not have any options to move it to another location. The export function results in a packaged file (zip), but importing is a crime, and so far i didn't get that to work.

  1: $moveFromUrl = "aSiteUrl"
  2: $newfolder = $site.OpenWeb().GetFolder("aFolderUrl")
  3: 
  4: $movefromList=$site.OpenWeb().GetList($moveFromUrl)
  5: 
  6: $docSet = [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSet]
  7:   ::GetDocumentSet($newfolder)  
  8: $compressedFile = $x.Export()
  9: 
 10: $compressedFile.GetType() 
 11: 
 12: $docsetID = [Microsoft.SharePoint.SPBuiltInContentTypeId]::DocumentSet
 13: $targetFolder = $movefromList.RootFolder
 14: $targetFolder.GetType()
 15: $user = $site.OpenWeb().EnsureUser("aUser")
 16: $user.GetType()
 17: 
 18: $properties = new-object System.Collections.Hashtable
 19: $properties.Add("DocumentSetDescription", "Description")
 20: 
 21: $z = [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSet]
 22:   ::Import($compressedFile, "DocsetRestore", $targetFolder, 
 23:   $docsetID, $properties, $user);

Results in a nice error telling me nothing.

  1: IsPublic IsSerial Name                 BaseType                                                                           
  2: -------- -------- ----                 --------                                                                           
  3: True     True     Byte[]               System.Array                                                                       
  4: True     False    SPFolder             System.Object                                                                      
  5: True     False    SPUser               Microsoft.SharePoint.SPPrincipal                                                   
  6: 
  7: Exception calling "Import" with "6" argument(s): "DocID: Site prefix not set."
  8: At :line:94 char:76
  9: + $z = [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSet]
 10:   ::Import <<<< ($compressedFile, "Docset1Backup", $targetFolder, 
 11:   $docsetID, $properties, $user);
 12: 

 

So I came up with a new approach, it is a Folder with a ContentType set to it, so I used the folder ‘MoveTo’ and updated the ContentType of the folder back to a ‘DocumentSet’, that lead to the same error I would do that through the browser, it still was a folder, without the DocumentSet welcome page set to it. After some debugging I found out that there was a field that differs between a folder and a DocumentSet: HTML File Type. So i made some changes and was able to move a DocumentSet with PowerShell. Below you can find the code, where you can see how we update the folder,in pt2 there will be some more info on how you can do so with a nice custom ribbon command, and also move all custom fields.

  1: param([string]$moveFromUrl, [string]$moveToUrl, [string]$moveItem)
  2: 
  3: [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") 
  4: 
  5: ##########################################################################
  6: ###                                                                    ###
  7: ###                            Functions                               ###
  8: ###                                                                    ###
  9: ##########################################################################
 10: 
 11: function checkItemToMove ([Microsoft.SharePoint.SPList]$movefromList, [Microsoft.SharePoint.SPList]$movetoList, [string]$moveItem)
 12: {
 13:   $docsetID = [Microsoft.SharePoint.SPBuiltInContentTypeId]::DocumentSet
 14:   [Microsoft.SharePoint.SPFolder]$docsetToMove = $null 
 15:   
 16:   foreach($docset in $movefromList.Folders)
 17:   {  
 18:     if($docset.ContentType.ID.ToString().StartsWith($docsetID.ToString()) -and $docset.Name -eq $moveItem) 
 19:     {
 20:       $docsetToMove = $docset.Folder
 21:       $docsetContentTypeId = $docset.ContentType.Parent.Id
 22:       break;
 23:     }       
 24:   }
 25:   
 26:   if($docsetToMove -ne $null -and $docsetContentTypeId -ne $null) 
 27:   {
 28:     Write-Host -ForegroundColor Green "Found a docset: " $docsetToMove.Name " Lets move it"
 29:     moveDocSet $docsetToMove $movetoList $docsetContentTypeId
 30:   }
 31:   else {  Write-Host -ForegroundColor Red "No document set of desired name found:" $moveItem }
 32: }
 33: 
 34: function moveDocSet ([Microsoft.SharePoint.SPFolder]$docset, [Microsoft.SharePoint.SPList]$movetoList, [string]$docsetContentTypeId)
 35: {
 36:   $moveurl = $movetoList.RootFolder.ToString() + "/" + $docset.Name  
 37:   
 38:   $docset.MoveTo($moveurl)
 39:   
 40:   #retrieve it at new location
 41:   [Microsoft.SharePoint.SPFolder]$newDocset=$site.OpenWeb().GetFolder($moveurl)
 42:   if($newDocset.Exists)   
 43:   {
 44:     #update it so it is a doc set and set CT right
 45:     $newDocset.Item["ContentTypeId"] = $docsetContentTypeId
 46:     $newDocset.Item["HTML File Type"] = "SharePoint.DocumentSet" 
 47:     #TODO update all custom fields .. 
 48:     $newDocset.Item.Update()
 49:   
 50:     Write-Host -ForegroundColor Green " Docset moved succesfully ... parteh "
 51:   }
 52:   else {Write-Host -ForegroundColor Red " Failed moving the docset or setting ... "}
 53: }
 54: 
 55: ##########################################################################
 56: ###                                                                    ###
 57: ###                           /Functions                               ###
 58: ###                                                                    ###
 59: ##########################################################################
 60: 
 61: Write-Host ""
 62: Write-Host -ForegroundColor Green "Move DocSet Script v1.0 - Albert-Jan Schot "
 63: Write-Host ""
 64: 
 65: if($moveFromUrl -eq $null -or $moveFromUrl -eq "") {Write-Host -ForegroundColor Red "No folder to move from"; Exit}
 66: if($moveToUrl -eq $null -or $moveToUrl -eq "") {Write-Host -ForegroundColor Red "No folder to move to"; Exit}
 67: if($moveItem -eq $null -or $moveItem -eq "") {Write-Host -ForegroundColor Red "No DocumentSet name set"; Exit}
 68: 
 69: #Retrieves the desired objects
 70: $site=new-object Microsoft.SharePoint.SPSite($moveFromUrl)
 71: 
 72: [Microsoft.SharePoint.SPList]$movefromList=$site.OpenWeb().GetList($moveFromUrl)
 73: [Microsoft.SharePoint.SPList]$movetoList=$site.OpenWeb().GetList($moveToUrl)
 74: 
 75: #Move a docset
 76: checkItemToMove $movefromList $movetoList $moveItem 

 

There is one remark, if you try to move a DocumentSet this way to a library that does not have all the ContentTypes available that are used in the DocumentSet you will have a problem with the Version. If all ContentTypes are available it will take the version history of the DocumentSet and move it, if the ContentTypes aren’t available it will try to copy the version history, but if you try to display it, it will fail, giving a field not present error, so keep that in mind.

Albert-Jan Schot schreef

Comments (0)

Albert-Jan Schot