Showing posts with label Roman Rylov. Show all posts
Showing posts with label Roman Rylov. Show all posts

Tuesday, May 26, 2015

SharePoint 2013 Designer and Workflow Tips, Tricks & Traps

In this article, I want to share my experience working with SharePoint Workflows via SharePoint Designer. Sometimes it may be very painful, but I hope after reading the article you will not step on same rake.

Do not log Dictionary variable to Log to History

This is very common issue, your workflow just stuck in started state with following error:

Retrying last request. Next attempt scheduled in less than one minute. Details of last request: HTTP InternalServerError to http://ift.tt/1cg6PXY(guid'04dc3f6a-968e-4ce0-a7cc-10949dfb15db')/Items Correlation Id: f54b9d7e-5242-1408-b066-5239a7b01c0a Instance Id: 967e7b1b-96cd-4fbf-b6d4-4df91199831b 
Invalid text value.

A text field contains invalid data. Please check the value and try again.
Retry now

It occur because your data is too big. This workflow action can display limited number of symbols. Otherwise such error occurs. As a workaround, you can use Send E-Mail with Attachments (SMTP) instead of log to history.

If you send dictionary by email you can receive it’s structure in JSON format. It is very helpful to understand the structure.


Be careful using Get an Item from Dictionary

Usually you need to get an item from array and you use ‘Get an Item from Dictionary’ workflow action like this:

However, please be careful because if you have extra whitespace in path, your workflow can be stuck into suspended status with following error:

RequestorId: 261892b4-cf71-e580-0000-000000000000. Details: An unhandled exception occurred during the execution of the workflow instance. 
Exception details: System.FormatException: The DynamicValue property '(0) ' was incorrectly formatted. at Microsoft.Activities.Dynamic.DynamicValueBuilder.PathSegmentFactory.Parse(String segmentText, String fullPathName) at Microsoft.Activities.Dynamic.DynamicValueBuilder.PathSegmentFactory.Create(String segment, PathSegment next, String path) at Microsoft.Activities.Dynamic.DynamicValueBuilder.PathSegmentFactory.Create(String path) at Microsoft.Activities.GetDynamicValueProperty`1.Execute(CodeActivityContext context) at System.Activities.CodeActivity`1.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager) at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation) Exception from activity GetDynamicValueProperty<DynamicValue> Sequence Microsoft.SharePoint.WorkflowServices.Activities.LoopNTimes Test WF Sequence Flowchart Sequence GetGroupMembers.WorkflowXaml_a9b1b4bd_9995_41a1_9da2_f8f57ce75c54

SharePoint Designer Cache Errors

The following error is very common when you work with SharePoint Designer:

“Failed to Load this workflow. To correct this problem, restart SharePoint Designer.” Failed to Load this workflow. To correct this problem, restart SharePoint Designer

To resolve it, you need to clear SharePoint Designer cache

And here are command for command line to do it:

rmdir "%LOCALAPPDATA%\Microsoft\WebsiteCache\" /s /q
rmdir "%APPDATA%\Microsoft\SharePoint Designer\ProxyAssemblyCache\" /s /q

Create self-documented workflow using stages

You should know how fast you can forget something what you did some time ago, when you are using workflow please try to use correct stage names and divide your workflow into small pieces of actions (like procedures in programming languages). In addition, you can use ‘Add a Comment’ workflow action to leave additional comments in your workflow.

As an example of the self documented workflow I can provide a notification workflow which I was described in my previous article.

Use Dynamic Value Evaluator to check your expression

It is useful when you use ‘Get an Item from Dictionary’, you can easily check your expression using Dynamic Value Evaluator. For example you have some data in your dictionary and want to build path to retrieve it. This is a tool which can help you to do it. Just past JSON structure of your dictionary into textbox and write your path, then click ‘Run’. It will show what result you can expect.

Dynamic Value Evaluator Dynamic Value Evaluator SharePoint Designer

Lookup User Name/Login/Email by ID

Indeed this is one of my favorite features you can use any string variable with integer value and lookup user data by ID. The example below returns correct display name of the user by specific ID.

You just have ID, but retrieve display name.

Lookup user name by ID

Get Date in required format

You can use out of the box method as on figure below:

SharePoint Designer Format Date Time

Additionally you can use free custom action ‘Format Date’ from Plumsail Workflow Actions Pack.

Use Requestb.in to check your query

When you use ‘Call HTTP Web Service’ you need to see how your request looks. RequestBin service can help you with that, it just logs all queries and shows them to you. 

Debug CALL HTTP Web Service workflow action

Use App Step to get access

This is hard to use feature, but in some situations, it is the only way to get required data. Moreover, you can use app-step to get data (for example via REST request) from other web. To use it you have to activate the feature that is called 'Workflows can use app permissions'. After that, you need to manually grant necessary permissions to your workflow via AppInv.aspx page. You can find more info on MSDN.

Conclusion

In this article, I have tried to collect most interesting notes, which I hope can save a huge amount of your time while creating your workflows. SharePoint 2013 workflows are quite flexible and allow you to use complex objects and loops, but sometime they just hit you to the stomach with some non-obvious errors.

The Original article is available on our blog


by Roman Rylov via Everyone's Blog Posts - SharePoint Community

Thursday, May 21, 2015

Query multiple lists across multiple sites and group results using SharePoint 2013 and Office 365 workflow

In this blog post I want to continue one of my previous articles Dynamic text and HTML templates using workflows for SharePoint 2013 or Office 365. Just in a few words to remind that in last article we have created a workflow that collects data from one list and makes two types of reports: individual and summary. These reports are delivered to user and administrator by email.

Now I will describe our new workflow action Get Items by Query (Many Lists). As you can see in documentation, it is an analogue of SPSiteDataQuery command which is exist in Server Object Model (SSOM) but it is not available in Client (CSOM). Moreover, it has additional features like grouping and sorting, which I will cover in this article.


Let’s imagine that we have something like Document Management System (DMS) based on SharePoint Online. Each our department has own site (HR, Sales, Finance, IT, etc…) and each department on own site has own document libraries structure specific to them. As in the last case, we want to build checked-out documents report, but now it should show data from all document libraries and from all sites.


In this article I will described how to query multiple lists across SharePoint sites. You can group results by specific field and then iterate through groups.

As a prof of concept I will create two workflows which send reports about checked out documents across multiple sites to individual users as well as summary report to administrator.

This is case where Get Items by Query (Many Lists) will be very useful, because it allows us to run one CAML query to many lists and as result, it will be single Dictionary object. 



Get Items by Query from many lists




Moreover, we can sort and group list items by one of fields. For example you group checked out documents by user. Result of query can be structured like this:



Get Items by Query from many lists and group results


Checked out documents – Administrators’ Report


So, let’s back to our Document Management System based on SharePoint. To build report by checked out documents for administrator we need to query all checked out documents and create composed email for administrator, below you can see the whole workflow process:



SharePoint Checked-out documents report


Click on the picture to enlarge it.


The workflow is divided into two parts. First part it is configuration. It contains email account and password. Also it contains Email Template which I provide below:


Please review the list of documents which are checked out:  <br /><br />
<table>
<thead>
<td><strong>User Name</strong></td><td><strong>Document</strong></td>
</thead>
<tbody>
{{#each Documents}}
<tr>
<td nowrap>{{CheckoutUser}}</td>
<td><a href="{{SiteUrl}}{{FileRef}}">{{FileLeafRef}}</a></td>
</tr>
{{/each}}
</tbody>
</table><br /><br />

Thank you, <br />
SharePoint Notification System. <br />


The second part of workflow contains main business logic. It is more interesting for us. To get required data I use Get Items by Query (Many Lists). The workflow action settings contains a few settings:

  • CAML Query - it is query that will be run on each list.
  • BaseTemplate - base template of the lists where the query will be run, in my case it is Document Library.
  • Web URLs - by default the query will be run on current site, but you can specify from where collect data. In my case I have specified web URLs of HR, Marketing and other subsites.

Below I provide a sample of CAML query which I use


<View Scope="RecursiveAll">  <ViewFields>
    <FieldRef Name="CheckoutUser"/>
<FieldRef Name="FileRef"/>
<FieldRef Name="FileLeafRef"/>
</ViewFields>
<Query>
<Where>
<IsNotNull>
<FieldRef Name="CheckoutUser"/>
</IsNotNull>
</Where>
<OrderBy>
<FieldRef Name="CheckoutUser"/>
<FieldRef Name="FileLeafRef"/>
</OrderBy>
</Query>
</View>

On next important step that is required for Render Text Template we build DataDictionary following way:



SharePoint Build a Dictionary Dynamic Value


I just created new variable and placed collection of checked out documents and site URL to separate properties of the dictionary. Now we can use this composed object as data for our HTML template.

The last step which I have to do before send email to administrator it is prettify ugly user name. To replace ‘ID;#’ to just dash I use Regular Expression Replace workflow action.


Checked out documents – Users’ Report


The second workflow should send individual notifications to users, it uses the same principles as previous, but the only difference it uses loop to iterate received data. The whole workflow you can see at the figure below:



SharePoint Checked-out documents user report



Click on the picture to enlarge it.


The loop iterates groups with selected users. This is required because I use 'group by' option in Get Items by Query (Many Lists) activity and our data looks like at the picture at the beginning of the article. Inside the loop, we receive documents which were checked out by this user and create individual email notification for the user.

Other steps are very similar to previous report and I don’t think I should explain it more. One little detail it is Build Dictionary workflow action, which is configured the following way:



SharePoint Build a Dictionary Dynamic Value


The CAML query in this workflow is the same as in previous workflow, but the HTML template is a little different.

Hello {{UserName}} <br /><br />
Please review the list below and please check-in unused documents:  <br />

<ul>
{{#each Documents}}
<li><a href="{{SiteUrl}}{{FileRef}}">{{FileLeafRef}}</a></li>
{{/each}}
</ul><br /><br />

Thank you, <br />
SharePoint Notification System. <br />

Conclusion


In this article I described how to query multiple lists across SharePoint sites. You can group results by specific field and then iterate through groups.

As a prof of concept I create two workflows which send reports about checked out documents across multiple sites to individual users as well as summary report to administrator.

I hope this example will help you to implement your own workflows much easier. Feel free to comment.


Original article is available at following link Query multiple lists across multiple sites and group results using SharePoint 2013 and Office 365 workflow
by Roman Rylov via Everyone's Blog Posts - SharePoint Community

Friday, March 6, 2015

How to invite external users using SharePoint Designer Workflows in Office 365 (Online)

In this article, I want to review a part of Customer Collaboration System, which can be used to share documents with clients and cooperative work on common projects. This ‘proof of concept’ was build using SharePoint Online and Plumsail Workflow Actions Pack.




Architecture description


Core of our system is a simple document library which contains different folders, each folder matches to one of clients and contains common documents (and other folders) which will be available for a client. The permission restriction will be carried out with help of SharePoint Groups. For each of our clients will be created own SharePoint group. Each folder in the document library will be matched to SharePoint group by name.




All internal logic concluded in two workflows, which start on “Item Creation” event. Additionally we will have two custom lists: "Companies" and "Contacts". We will use these lists as dictionaries to store information about clients. “Contacts” list contains user email and lookup to “Companies” List.




The common architecture scheme I have provided below:


Invite External Users SharePoint Online



How does it look for end user?


For example, we want to share contract with our contractor. Firstly, we need to add contractor’s company in “Companies” list and add a couple of contractor’s employee into “Contacts” list. Right after that, we can upload any documents inside contractor’s folder in the documents library. All contractor’s employee will get an email invitation that look like at the figure below and will have access to shared documents.


Invite External Users SharePoint Online Invitation



Create new Company workflow


The “Create new company” workflow is very simple it does only three things:


  • Creates new SharePoint group with name of company;

  • Creates new folder in “Documents” library;

  • Grants permission on created folder;




This workflow is configured to start on list item creation in “Companies” list.


Invite External Users SharePoint Online Workflow

There is only one moment on which I want to pay attention, it is parameter "Folder ID". It was filled the following way. We need to know folder ID to grant permission to folder by a workflow action.


Invite External Users SharePoint Online Workflow



Invite External Users Workflows


There is another very simple workflow, which contains only a few Workflow Action, please pay attention that I used Invite External Users workflow action.


This workflow is configured to start on list item creation in “Companies” list.


Invite External Users SharePoint Online Workflow

Before using this workflow action please turn on option “Allow external users who accept sharing invitations and sign in as authenticated users” in site collection administration interface (usually it at the following URL: “https://yousitehttp://ift.tt/1FjiQFq”).




Conclusion


In this short article I briefly reviewed a part of implementation of Custom Collaboration System, based on SharePoint Online and Workflow Actions Pack. I hope you will go ahead and do more complex systems. Additionally I want to mention that using external users allows you to save your money on licenses for Office 365 users.




Please feel free to comment the article, I am curious about your cases and implementations.




Original article is available in our blog.

by Roman Rylov via Everyone's Blog Posts - SharePoint Community

Monday, February 23, 2015

How to send periodic expiration email notifications in Office 365 and SharePoint 2013 Workflows

Introduction


In this article, I want to cover very common scenario of any business system it is alert notification or reporting subsystem. Usually that functional is responsible to notify end users about new events, overdue tasks and sending summary reports via e-mail. I want to review a couple of simple cases. Which I hope, you can manually customize and implement in your environment. All cases I created are use SharePoint Online with installed Plumsail Workflow Actions Pack, but you can implement it in OnPremise environment as well.




Personal Item/Task/Document expiration reminders


The first case which I want to describe has quite simple simple goal: a user wants to track status of sent Item/Task/Document. It could be application form for business trip, day off request, assigned task or something else. It depends on your business process, but core idea is the same. You have an item with specific due date and a person responsible for this item. You need to send notification message to the user when due date will come and of course you can customize the message template. Using SharePoint Designer you can create a site level workflow which will select all list items by specific query and process each item. As an example, it could generate email by template and send it.




Out of the box workflow actions don’t allow to query list items by CAML or generate email messages by templates. That is why I used some workflow actions from Workflow Actions Pack to extend out of the box functionality.




Below I showed the configured workflow:






I used the following CAML query to get all list items that expire in two days in Get Items by query workflow action:



<View>
<ViewFields>
<FieldRef Name='ID' />
<FieldRef Name='Title' />
<FieldRef Name='DueDate' />
<FieldRef Name='AssignedTo' />
</ViewFields>
<Where>
<And>
<Neq>
<FieldRef Name='Status' />
<Value Type='Choice'>Completed</Value>
</Neq>
<Lt>
<FieldRef Name='DueDate' />
<Value Type='DateTime'><Today OffsetDays='2' /></Value>
</Lt>
</And>
</Where>
</View>




Send email workflow action configuration (to simplify the process I designed the email template inside):



Hello [%Variable: userField%] <br /><br />
Have you had time to look into this? <br /><br />

<a href=”http://ift.tt/1w1zwvX Tasks/DispForm.aspx?ID=[%Variable:ListItemID%]”>[%Variable:ListItemTitle%]</a><br />
The Due Date is [%Variable: DueDate%]

<br /><br />
Kind Regards, <br />
SharePoint Notification System




You can see the sceenshot below as result:








Daily/Weekly/Mounthly aggregated expiration reports by email




The second example which I want to show is based on the same algorithm, but you need to get aggregated summary report instead of sending notifications to each user. To do this you can get all data and use Render Template workflow action to build complete email by predefined template.


Below I implemented the workflow for typical case when an administrator wants to get a summary report with a list of all checked out documents ordered by user.


At the figure below you can see the configured workflow, I split it on two stages to simplify configuration and copying to other document libraries.






The first step is just configuration section.

Next, I got all list items where the field CheckoutUser is not null, I used the following CAML query for this:


<View Scope="RecursiveAll"> <ViewFields>
<FieldRef Name="CheckoutUser"/>
<FieldRef Name="FileRef"/>
<FieldRef Name="FileLeafRef"/>
</ViewFields>
<Query>
<Where>
<IsNotNull>
<FieldRef Name="CheckoutUser"/>
</IsNotNull>
</Where>
<OrderBy>
<FieldRef Name="CheckoutUser"/>
<FieldRef Name="FileLeafRef"/>
</OrderBy>
</Query>
</View>




After it, I prepared data for the Render Text Template workflow action. I used the following template:



Please review the list of documents which was checked out: <br /><br /><table>
<thead>
<td><strong>User Name</strong></td><td><strong>Document</strong></td>
</thead>
<tbody>
{{#each Documents}}
<tr>
<td>{{FieldValues.CheckoutUser}}</td>
<td><a href="{{SiteUrl}}{{FieldValues.FileRef}}">{{FieldValues.FileLeafRef}}</a></td>
</tr>
{{/each}}
</tbody>
</table><br /><br />

Thank you, <br />
SharePoint Notification System. <br />




Before sending the message I used Regular Expression Replace workflow action to replace values in the User’s field from value like 24#;John Martin to 24 – John Martin.


And at the final step I sent complete email report to an administrator








Conclusion


In this article I wanted to describe some common cases which can be used for alerting or reminding purposes. I hope it was clear enough and it will not be difficult to repeat it or customize to your own requirements. Please feel free to comment the article.




Origianl posted at How to send periodic expiration email notifications in Office 365 and SharePoint 2013 Workflows

by Roman Rylov via Everyone's Blog Posts - SharePoint Community

Wednesday, February 18, 2015

How to Start SharePoint 2013 Workflows on Selected Items

This blog post appeared as an email response to one of our clients, he described very common use case where you have two libraries of documents, the first library where the users works with draft documents and the other library which is stored the final versions of documents. In this case, users want to be able to select finished documents and move it in one click. Oblivious solution for this is to use a workflow that will move documents to the final document library. But there is one question how we can start the workflow on selected list items? When I was thinking about the answer to that question - I realized that is not as trivial as it seems, because list level workflow can be started only on singl document. How to start multiple workflows on selected documents? In the article, I will describe how you can implement such solution.


I divided this article into three main parts:



  • Get selected items via JavaScript;

  • Start SharePoint 2013 workflow from JavaScript;

  • Add custom button to the list ribbon




Before we start, I want to show what we will get in result. On the figure below you can see the custom button on the ribbon, which will launch a workflows on selected documents.








How to get selected elements via JavaScript


Firstly I will describe how you can get selected List items from SharePoint list. To do this, you can use special JavaScript function getSelectedItems. Please see example below:



var ctx = SP.ClientContext.get_current();var selectedItems = SP.ListOperation.Selection.getSelectedItems(ctx);

for (item in selectedItems)
{
var itemId = selectedItems[item].id;
console.log(itemId);
}




As result of execution, this snippet of code you should see the IDs of selected list items in the console log.




How to start workflow


Since SharePoint 2013 Microsoft has added a new workflow engine. The new JavaScript API for manipulation with workflows also became available. In this article, I will not describe it, but if you are interested, you can find more information from the Andrei Markeev’s blog post. I will show you ready to use JavaScript snippet, which can run workflow on a single list item.



function startWorkflow(itemID, subID) { var context = SP.ClientContext.get_current();
var web = context.get_web();
var wfServiceManager = SP.WorkflowServices.WorkflowServicesManager.newObject(context, web);
var subscription = wfServiceManager.getWorkflowSubscriptionService().getSubscription(subID);

context.load(subscription);
context.executeQueryAsync(
function(sender, args){
console.log("Subscription load success. Attempting to start workflow.");
var inputParameters = {};
wfServiceManager.getWorkflowInstanceService().startWorkflowOnListItem(subscription, itemID, inputParameters);

context.executeQueryAsync(
function(sender, args){ console.log("Successfully starting workflow."); },
function(sender, args){
console.log("Failed to start workflow.");
console.log("Error: " + args.get_message() + "\n" + args.get_stackTrace());
}
);
},
function(sender,args){
console.log("Failed to load subscription.");
console.log("Error: " + args.get_message() + "\n" + args.get_stackTrace());
}
);
}




This simple JavaScript function launches a workflow on a list item. The workflow subscription is specified by SubID argument. To identify the subscription ID of your workflow you can navigate to the workflows start page and see the URL in your browser, it should look like this (the bold GUID it is workflow subscription ID): “javascript:StartWorkflow4('6eb43e78-6e6c-486a-9147-3e3870f3a44e', '19', '{FA41C64B-42CD-4A3F-A1AF-CF674AB35C57}')”






How to Add a Button to the SharePoint Ribbon


Very important part is how a user will interact with our system, I believe that the user experience is very important for any system. In our case we plan that the user will select the documents and click on the ribbon button which will move them to another document library.


There are various ways to add a button on the ribbon, but in this article, I want to describe the simplest. We will use SharePoint Designer to do this. When we use SharePoint Designer we have some limitation for example we cannot add new ribbon tab or hide any existing button, but from other point of view if we need just to add button to the ribbon it will take five minutes of our time and it doesn't require programming skills.


Please open the SharePoint Designer, navigate to “List and Libraries” and choose your document library.






To add a ribbon button please click inside the “Custom Actions” area and choose on the ribbon “Custom Actions” – “View Ribbon”.


When you fill in the all fields, you should see something like on the figure below in your list.






Please pay attention at the property “Navigate URL”, I filled it with following text: javascript:PlumsailDemo.WFPack.API.StartListWorkflowOnSelectedItems("{6eb43e78-6e6c-486a-9147-3e3870f3a44e}");




This is a call of our function with argument “Subscription ID” of our workflow.




Join all of it together


OK, we almost did it. All we need to do it is combine and place the JavaScript in SharePoint. To add our JavaScript on the page we will use the ScriptEditor Web part. To do this you need to enter to edit page mode, click on “Add a Web Part” and select “Script Editor” in “Media and Content” group.




I changed the JavaScript file little bit to simplify using



<script type="text/javascript">// <![CDATA[var PlumsailDemo = PlumsailDemo || {};
PlumsailDemo.WFPack = PlumsailDemo.WFPack || {};

PlumsailDemo.WFPack.API = (function() {
var self = this;
self.Context = null,
self.WFManager = null;

SP.SOD.executeFunc("sp.js", "SP.ClientContext" , function(){
SP.SOD.registerSod('sp.workflowservices.js', SP.Utilities.Utility.getLayoutsPageUrl('sp.workflowservices.js'));
SP.SOD.executeFunc('sp.workflowservices.js', "SP.WorkflowServices.WorkflowServicesManager",
function() {
self.Context = SP.ClientContext.get_current();
var web = self.Context.get_web();
self.WFManager = SP.WorkflowServices.WorkflowServicesManager.newObject(self.Context, web);
});
});

StartListWorkflowOnSelectedItems = function(subID) {
var selectedItems = SP.ListOperation.Selection.getSelectedItems(self.Context);

for (item in selectedItems)
{
var itemId = selectedItems[item].id;
self.StartListWorkflow(itemId, subID);
}
};

StartListWorkflow = function(itemID, subID) {
var subscription = self.WFManager.getWorkflowSubscriptionService().getSubscription(subID);

self.Context.load(subscription);
self.Context.executeQueryAsync(
function(sender, args){
var inputParameters = {};

self.WFManager.getWorkflowInstanceService().startWorkflowOnListItem(subscription, itemID, inputParameters);

self.Context.executeQueryAsync(
function(sender, args){
var message = "The workflow " + subscription.get_name() + " was started on item with ID " + itemID;
SP.UI.Notify.addNotification(message, false);
},
function(sender, args){
var message = "Failed to start workflow " + subscription.get_name() + " on item with ID " + itemID;
SP.UI.Notify.addNotification(message, false);
console.log("Failed to start workflow.");
console.log("Error: " + args.get_message() + "\n" + args.get_stackTrace());
}
);
},
function(sender,args){
var message = "Failed to load subscription " + subID;
SP.UI.Notify.addNotification(message, false);
console.log("Failed to load subscription.");
console.log("Error: " + args.get_message() + "\n" + args.get_stackTrace());
}
);

};

return {
StartListWorkflow: StartListWorkflow
, StartListWorkflowOnSelectedItems: StartListWorkflowOnSelectedItems
};
})();
// ]]></script>




Conclusion


In the article, we reviewed the way how you can start multiple workflows on the selected list items. As conclusion, I want to mention about one little detail, in such approach very difficult to monitor the status and errors of the workflows because it works on multiple list items. But you can extend my simple JavaScript with some workflow tracking logic if you have enough JavaScript/SharePoint skills.




You have to understand what will be the load for the system, for small systems this is an acceptable approach, but for highly loaded systems this is not so good. You can consider other approaches for moving documents like single site level workflow or custom coded solution.


As alternative of this approach, you can start a site level workflow and pass to it the selected IDs, but this is the theme of another article.




Please feel free to comment, I will be happy to answer to your questions.




Original article available at our blog

by Roman Rylov via Everyone's Blog Posts - SharePoint Community

Tuesday, February 3, 2015

Researching Site Template Structure with SPD workflows

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 customize it very deep (add new lists, views, workflows, customize UI) and finally you will get 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 customized site to WSP package and deploy the package to the customer's environment.


The issue with deployment of template with custom workfllow 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 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 site as template. Thus, you have site template and 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 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 prefered 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 inner content of WSP package we should understand structure of it.

As you maybe already know any WSP package is renamed CAB archive. If we open it we should see something like this:


Structure of WSP package


The main file “manifest.xml” contains the list of features, each feature is located in 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://ift.tt/sQmbje">
<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 list of features that will be activated during deployment process. Apart from features in 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 on 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 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 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 featuresvar 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:


Conclusion


As conclusion of this small research we've got a small console utility that will help you to prepare WSP package to deploy to other SharePoint environment.


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


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


P.S. If you're not a developer and you want to just solve the issue, you can read the following my article where I showed how you can use this small utility.


P.S.S.:Original link to article Researching Site Template Structure with SPD workflows




by Roman Rylov via Everyone's Blog Posts - SharePoint Community