logo
Documents & Forms
Microsoft 365 & SharePoint Tools
Classic SharePoint Tools
Actions Pack
Nov 15, 2017

Researching Site Template Structure with SPD workflows

Customer Support Engineer

Introduction

Usually, SharePoint development process looks as follows. You create a new site which is based on one of the standard SharePoint templates (Team Site, Blog, Project Site, etc.) after that, you work hard and deeply customize it (add new lists, views, workflows, customize UI) and finally you will get a complete business solution.

But there is another question: “How to deliver your solution to a client?”. One of the answers to this question is to use site templates. You can save the customized site to WSP package and deploy the package to the customer’s environment.

The issue with deployment of template with custom workflow actions

Looks very interesting and useful, but in SharePoint’s world, the Devil is in the detail. For example, if you’re using custom workflow actions in your workflows you will get an error during deployment process:

Microsoft.Workflow.Client.ActivityNotFoundException: The activity named 'WorkflowXaml_988fe3c3_dc3e_47a9_8ff3_c912c82eb56d' from scope '/spo/216722f1-1dc0-4c6d-aafd-86b12ef60a14/1f87e1ae-da77-44d4-92d1-502507b5f2d4/34ceaf02-5217-46be-a307-e09dd93da230' was not found. HTTP headers received from the server - ActivityId: 224e672a-4e00-44ed-8b55-894a059608c2. NodeId: . Scope: . Client ActivityId : f6cadd9c-c011-1000-aa42-85eedd28fc19. ---> System.Net.WebException: The remote server returned an error

This not informative message means that the SharePoint tried to publish your workflow, but it can’t find custom workflow action. I saw this issue for several of our clients and after research, we have found the reason. It turned up that during deployment process SharePoint doesn’t trigger feature event receivers. Event receiver doesn’t deploy workflow actions and SharePoint throws an error during publishing process (because it can’t find workflow actions used in the workflow).

As result of our research, we have found two workarounds how to solve the issue and deploy site template with workflows including custom actions.

The first way: You need to save workflows as templates. Then remove them from the site (you are always able to restore them from saved templates). Deactivate a feature which contains custom actions (In my case it is Workflow Actions Pack) and save the site as a template. Thus, you have the site template and the templates of workflows as separate WSP packages. To deploy such site you need to create the new site using your template (without workflows). Then activate feature with your custom actions. Then deploy site workflows templates which you saved earlier.

The second way: You can save the site as a template and manually edit WSP package. You need to move workflows to another feature that will be activated manually, also you need to remove workflow actions from modules (because they are deployed via separate WSP package). This is the preferred way, but it requires strong developers skills. I will describe it below. And together we will try to automate the solution.

Typical structure of a WSP package

Before can start playing with the inner content of WSP package we should understand its structure.
As you maybe already know any WSP package is renamed CAB archive. If we open it we should see something like this:

1

The main file “manifest.xml” contains the list of features, each feature is located in a separate folder. You may notice that SharePoint divided features by executed functions (CustomActions, ListInstanes, PropertyBags, etc..)
The feature definition files are “Feature.xml” and “Elements.xml”. Additionally, a feature may deploy other files, all deployable files are described in the feature definition file.

Typical feature definition looks like:

<Feature  Id="{98ae7648-c63b-4473-8921-43355f83e14e}"
    Title="User custom workflows"
    Description="Custom user workflows"
    Version="1.0.0.0"
    Scope="Web"
    Hidden="FALSE"
    xmlns="http://schemas.microsoft.com/sharepoint/">
   <ElementManifests>
    <ElementManifest Location="Elements.xml" />
    <ElementFile Location="Files\wfsvc\988fe3c3dc3e47a98ff3c912c82eb56d\workflow.xaml" />
    <ElementFile Location="Files\wfsvc\988fe3c3dc3e47a98ff3c912c82eb56d\WorkflowAssociation_3d5de7c53c1347fc9779e48926f1d0ef" />
    <ElementFile Location="Files\wfsvc\ee7bc602661d442b9ea00f08d09594e8\workflow.xaml" />       
    <ElementFile Location="Files\wfsvc\ee7bc602661d442b9ea00f08d09594e8\WorkflowAssociation_2351a8b91c354902ab1b6976138d5546" />
   </ElementManifests>
</Feature>

As you can see it is pretty simple. The Feature has a Title, ID, Description, link to manifest and a list of included files. If you want to know a little bit more you can read MSDN documentation.

Additionally, I want to highlight ONet.xml file, it contains the list of features that will be activated during deployment process. Apart from features in the package it contains references to dependent features from other packages. An example from this file is shown below:

<!--BaseWeb Feature-->
<Feature ID="{99fe402e-89a0-45aa-9163-85342e865dc8}" Name="FeatureDefinition/15/99fe402e-89a0-45aa-9163-85342e865dc8" SourceVersion="15.0.0.3" />
<!--Plumsail.ActionsPack_Plumsail.ActionsPack Feature-->
<Feature ID="{d7891031-e7f5-4734-8077-9189dd35551c}" Name="FeatureDefinition/15/d7891031-e7f5-4734-8077-9189dd35551c" SourceVersion="0.0.0.0" />
<!--SiteFeedController Feature-->
<Feature ID="{5153156a-63af-4fac-b557-91bd8c315432}" Name="FeatureDefinition/15/5153156a-63af-4fac-b557-91bd8c315432" SourceVersion="15.0.0.0" />
<!--WorkflowTask Feature-->
<Feature ID="{57311b7a-9afd-4ff0-866e-9393ad6647b1}" Name="FeatureDefinition/15/57311b7a-9afd-4ff0-866e-9393ad6647b1" SourceVersion="1.0.0.0" />

The Solution

Now when we studied the structure of typical WSP package we can move forward. I want to write a simple console utility which will repair the WSP package and it will move all workflows to separate feature.
To start we will divide all work into separate stages:

  • Create new directory for workflows feature
  • Create Feature.xml and Elements.xml definitions for the workflow feature
  • Browse all other features and remove custom workflow actions (because the workflow actions are deployed via separate WSP package, if we don’t do it in SharePoint Designer we will see duplicated workflow actions)
  • Walk through all other features and move workflows into our new workflow feature.
  • Repackage WSP package.

To ensure that the solution will work we can walk through these steps manually, but the next step we will automate this process and in next part of the article I will show you the main functions of the console program that you can use.

I do not want to clutter the article with a lot of code and I will just show examples of the main functions.
To add a new feature we will create Feature.xml and Elements.xml in a separate folder inside WSP package.

Firstly we need to create new workflows feature, to do this I used the following code:

void CreateWorkflowFeature()
{
    wfFeatureDir = Path.Combine(BasePath, WFFeaturePath);
    System.IO.Directory.CreateDirectory(wfFeatureDir);
    wfFeature = new XDocument(
        new XElement(ns + "Feature",
            new XAttribute("Id", Guid.NewGuid().ToString()),
            new XAttribute("Title", "Template workflows"),
            new XAttribute("Description", "Template workflows"),
            new XAttribute("Scope", "Web"),
            new XAttribute("Version", "1.0.0.0"),
            new XElement(ns + "ElementManifests",
                new XElement(ns + "ElementManifest", new XAttribute("Location", "Elements.xml")))
            ));

    wfElements = new XDocument(new XElement(ns + "Elements"));
}

Then we need to process the manifest file and iterate through the all features:

var manifestFilePath = Path.Combine(BasePath, "manifest.xml");

//Load features
var manifest = XDocument.Load(manifestFilePath);
var features = manifest.Descendants(ns + "FeatureManifest");

//Process features list defined in manifest.xml
foreach (var featureDef in features)
{
    var featureFile = Path.Combine(BasePath, featureDef.Attribute("Location").Value);
    ProcessFeature(featureFile);
}

For each feature, I call a list of working methods that will process a feature individually

void ProcessFeature(string featureFile)
{
    var featureDir   = Path.GetDirectoryName(featureFile);
    var elementsFile = Path.Combine(featureDir, "Elements.xml");
 
    var feature  = XDocument.Load(featureFile);
    var elements = XDocument.Load(elementsFile);
 
    RemoveCustomActions(feature, elements, featureDir);
    RemoveModules(feature, elements, featureDir);
    RemoveDependentFeatures(feature, elements, featureDir);
    MoveWorkflowModules(feature, elements, featureDir);
 
    //Save files
    elements.SaveToFile(elementsFile);
    feature.SaveToFile(featureFile);
}

The main thing here in the methods RemoveCustomActions, RemoveModules, RemoveDependentFeatures and MoveWorkflowModules. They work with input XElements and also move files physically. Then we just save the Feature and Elements files.

As result of our work the structure of WSP package will look as follows:

2

Conclusion

As aconclusion of this small research, we’ve got a small console utility that will help you to prepare WSP package to deploy to another SharePoint environment.

All source code is available on GitHub and you can research it deeper or write something useful for your case.

Feel free to comment the article. I will be glad to answer interesting questions.

P.S. If you’re not a developer and you want to just solve the issue, you can read the following article where is shown how you can use this small utility.

Note: The post has been originally published at Medium: