Custom Workflow Activity for Setting the Associated Members Group of a SharePoint Site

So far in this series of posts I've looked at building custom workflow activities in Visual Studio 2012 to create sites, create groups, and set group owners in SharePoint 2013. In this post I'll walk you through how to build a workflow activity that sets the associated members group of a SharePoint site. You can use the same approach to set the associated owners group and the associated visitors group as required.

Note: this is part of a series of posts on building workflow activities to manage sites, groups, users and permissions. For a complete list of posts, together with a more detailed walkthrough of how to build a custom workflow activity and make it available in SharePoint Designer, start at the beginning.

Fundamentals

You can't currently use the REST API to set the associated groups for a SharePoint site. The REST API includes endpoints for each property:
  • /_api/web/associatedownergroup
  • / _api/web/associatedmembergroup
  • /_api/web/associatedownergroup
However, although these work fine for GET requests, there doesn't currently appear to be any way of changing these properties. As an alternative, you can set associated groups by calling the client.svc service and including an XML body that specifies the changes you want to make. (For a bit more background information on CSOM XML, take a look at my previous post.)

If you want to set the associated members group for a SharePoint site, you need to send a web request that resembles the following:

Endpoint:
{web URL}/_vti_bin/client.svc/ProcessQuery

HTTP method:
POST

Headers:
Content-Type: text/xml

Body:
<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="15.0.0.0" ApplicationName=".NET Library" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009">
   <Actions>
      <SetProperty Id="1" ObjectPathId="2" Name="AssociatedMemberGroup">
         <Parameter ObjectPathId="3" />
      </SetProperty><Method Name="Update" Id="4" ObjectPathId="2" />
   </Actions>
   <ObjectPaths>
      <Identity Id="2" Name="{0}:site:{1}:web:{2}" />
      <Identity Id="3" Name="{0}:site:{1}:g:{3}" />
   </ObjectPaths>
</Request>

Again - for a more detailed explanation of the elements in these CSOM XML requests, take a look at the previous post.

The XML body shown above includes various string placeholders that you'll need to replace before you call the service:
  • {0} is the GUID of the SPObjectFactory class (always 740c6a0b-85e2-48a0-a494-e0f1759d4aa7).
  • {1} is the GUID of the current SPSite (i.e. the SPSite that contains your SPWeb).
  • {2} is the GUID of the SPWeb on which you want to set the property.
  • {3} is the integer ID of the SharePoint group you want to associate with the site.
If you want to set the associated owners or visitors group, you can use essentially the same XML body - simply replace the text AssociatedMemberGroup with AssociatedOwnerGroup or AssociatedVisitorGroup.

Build the XAML File

At this point I'll assume that you know the basics of how to build custom workflow activities for SharePoint 2013 - if you want a more detailed walkthrough, take a look at my first post in this series. I'll start by defining the arguments:










In this case, I need the consumer of the workflow activity to specify the URL of the site they want to modify, together with the integer ID of the group they want to associate with the site. The activity will return the status code from the web service response.

Next, I'll define the variables I want to use within the activity:
















You'll see the purpose of each of these as we walk through the activity. The complete activity looks like this:








































In this case, we've got a total of eight child activities. I'll walk through each of these in turn.

Step 1: Assign (build service URL)
In this activity, we want to build a service endpoint for our request. I've used an Assign activity to do this. In the activity properties, I'm concatenating the site (SPWeb) URL provided by activity consumer and the site-relative service endpoint, and assigning it to the serviceUrl variable.














Step 2: Assign (define xmlFormatString)
In this activity we build a format string for our XML body with placeholders for our GUIDs and group IDs. I've used an Assign activity to do this:













The value here is the XML body I showed you at the start of this article, with whitespace removed and quotation marks escaped:
@"<Request AddExpandoFieldTypeSuffix=""true"" SchemaVersion=""15.0.0.0"" LibraryVersion=""15.0.0.0"" ApplicationName="".NET Library"" xmlns=""http://schemas.microsoft.com/sharepoint/clientquery/2009""><Actions><SetProperty Id=""1"" ObjectPathId=""2"" Name=""AssociatedMemberGroup""><Parameter ObjectPathId=""3"" /></SetProperty><Method Name=""Update"" Id=""4"" ObjectPathId=""2"" /></Actions><ObjectPaths><Identity Id=""2"" Name=""{0}:site:{1}:web:{2}"" /><Identity Id=""3"" Name=""{0}:site:{1}:g:{3}"" /></ObjectPaths></Request>"


Step 3: Get SPObjectFactory ID
When we construct the XML body for our service request, we need to include the GUID of the SPObjectFactory class in all our object paths. As I mentioned earlier, in the current version of SharePoint this is always 740c6a0b-85e2-48a0-a494-e0f1759d4aa7. Here, I've created a simple helper activity that returns the SPObjectFactory ID. I encapsulated it in a helper activity in case the GUID changes at a later date.











Step 4: Get Site Guid
Our service request XML body must also include the ID of the site collection that contains our group and its new owner. Here, I've created a simple helper activity that returns the GUID ID of the current site collection:












If you've got this far, you probably won't have any difficulty getting the site GUID. Essentially, you send a GET request to {site collection URL}/_api/site, and then retrieve the "d/Id" property from the response.

Step 5: Get Specified Web Guid
Our service request XML body must include the ID of the site (SPWeb) you want to modify. Bear in mind that this may not be the current SPWeb - if you're building a workflow that creates and configures a subsite, your workflow is likely to be running on the root site of the site collection, whereas the subsite you're configuring will be elsewhere within the site collection. Here, I've created a simple helper activity that returns the GUID ID of a specified SPWeb:














The activity takes the web URL provided by the activity consumer and returns the ID of the web. Again, getting the GUID of an SPWeb is pretty straightforward - you send a GET request to {site URL}/_api/web, and then retrieve the "d/Id" property from the response.

Step 6: Assign (populates xmlBody)
In this activity, we plug our variable values into our format string. This gives us a complete XML body to the web service:












The value here is as follows:
String.Format(xmlFormatString, factoryGuid.ToString(), siteGuid.ToString(), webGuid.ToString(), groupId.ToString())


Step 7: HttpSend
At this point, you've set all the variable values you need in order to call the web service. The HttpSend activity is configured as follows:
























This is pretty straightforward - you're sending a POST request containing your XML body to the service URL you defined near the start of the activity. The only other thing you need to do at this point is to add a Content-Type header:



















Step 8: Assign responseStatusCodeOut
In this final task, we're simply converting the response status code into an SPD-friendly format and assigning it to an argument:













Build the Actions File

The last task is to edit the .actions4 file so we can use the Set Associated Members Group activity in SharePoint Designer. To recap, the .actions4 file defines the sentence that appears in SPD when you add the activity to a workflow, together with the arguments defined in the workflow activity. If you've made it this far, this step should be pretty straightforward (For a more detailed explanation of what's going on in the .actions4 file, refer to the first post in this series.)

In this case, the .actions4 file should resemble the following:
<Action Name="Set Associated Members Group" ClassName="SiteManagementActivities.SetMembersGroup" Category="Site Management" AppliesTo="all">
  <RuleDesigner Sentence="Make group %1 the associated members group of the SPWeb at %2 (Output: %3)">
    <FieldBind  Field="groupId" Text="Group ID" Id="1" />
    <FieldBind  Field="webUrl" Text="Web URL" Id="2" />
    <FieldBind  Field="responseStatusCodeOut" Text="Response Status Code" Id="3" />
  </RuleDesigner>
  <Parameters>
    <Parameter Type="System.Int32, mscorlib" Direction="In" Name="groupId" />
    <Parameter Type="System.String, mscorlib" Direction="In" Name="webUrl" />
    <Parameter Type="System.String, mscorlib" Direction="Out" Name="responseStatusCodeOut" />
  </Parameters>
</Action>

And that concludes how to build a workflow activity that sets the associated members group of a SharePoint site. I hope somebody out there finds it useful.

Update 17/11/14: a sample Visual Studio solution containing these workflow activities is now available for download.

Comments

  1. Hi,
    Can you please let me know how to set associatedOwnerGroup with REST API to new site which does not have any group. I have created new group & able to Add Role. But I am not able to get any help related to set associatedOwnerGroup with REST API.
    I am working on SharePoint Online.

    Regards,
    Gaurav

    ReplyDelete
    Replies
    1. You can't do this with the REST API. This post explains how to use the client.svc web service as a workaround. This approach is fully compatible with SharePoint Online.

      Delete
  2. Great post thanks for sharing. Quick question you said "You can't currently use the REST API to set the associated groups for a SharePoint site" has this been addressed and fixed by MS? According to this documentation: https://msdn.microsoft.com/en-us/library/office/jj245638.aspx?f=255&MSPPError=-2147217396
    It looks like you can do a POST request. Thanks =)

    ReplyDelete
    Replies
    1. Yes, it looks like you should be able to. Unfortunately, while the endpoint does accept POST requests, I've yet to find a request format that actually works (and I've tried quite a few!) For the time being it looks like I'll have to stick with client.svc.

      Delete

Post a Comment

Popular posts from this blog

Server-side activities have been updated

The target principal name is incorrect. Cannot generate SSPI context.

Custom Workflow Activity for Creating a SharePoint Site