Send rich emails with attachments from a SharePoint Designer Workflow

Posted at: 13:49 on 29 December 2009 by Muhimbi

emailOne of the most common (sub) tasks executed from a SharePoint workflow is the submission of an email. SharePoint ships with an Email Workflow Action out of the box, which is unfortunately very limited in its abilities. For example, it is not possible to include attachments, specify the From Address, set the Priority or specify the SMTP server to use.

In this blog post I’ll describe how to send an email from a workflow that automatically includes all files / attachments for the current item from a SharePoint Designer workflow using the Workflow Power Pack.

A quick introduction for those not familiar with the product: The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.

Please note that this tutorial was originally written for SharePoint 2007. In newer versions of SharePoint please replace the ‘Build Dynamic String’ action with the ‘Set Workflow Variable’ action. Some familiarity with building basic workflows in SharePoint Designer and SharePoint programming in C# is required. Please see the Workflow Power Pack User Guide series for more details.

The solution presented below uses the standard Build Dynamic String Action to create the body of the email. This body is then passed as Parameter 1 to the Execute Custom Code Action. Parameter 2 is used to specify the To Address. This code is a great starting point for further customisation. For more details see the standard .net MailMessage and SmtpClient classes.

Create the workflow as follows:

  1. Download and install the Muhimbi Workflow Power Pack for SharePoint.
     
  2. Make sure you have the appropriate privileges to create workflows on a site collection.
     
  3. Create a new workflow using SharePoint Designer.
     
  4. On the Workflow definition screen associate the workflow with the list or library of your choice, tick the boxes next to both ‘Automatically start… ’ items and proceed to the next screen.
     
  5. Click the Actions button and insert the Build Dynamic String action.
     
  6. Click dynamic string and enter an HTML based email.
     
  7. Click variable1 and create a new string based variable named mailMessage.
     
  8. Click the Actions button and insert the Execute Custom Code action.
     
  9. Click parameter 1, open the Workflow Lookup dialog and select Source: Workflow Data, Field: mailMessage.
     
  10. Click parameter 2 end enter the address to send the email to. Naturally this could be a lookup value as well.
     
  11. Insert the following C# based code by clicking this code.
     
    using System.Net.Mail;
    using System.IO;
     
    string smtpServer = MyWorkflow.Site.WebApplication.OutboundMailServiceInstance.Server.Address;
    SPListItem item = MyWorkflow.Item;
     
    // ** Get the standard sender address for the Web App. 
    // ** Feel free to replace with an address of your choice.
    string from = MyWorkflow.Site.WebApplication.OutboundMailSenderAddress;
    string to = (string) MyWorkflow.Parameter2;
    string subject = "New SharePoint Files";
    string body = (string) MyWorkflow.Parameter1;
    // ** Process any 'workflow variables' that may exist in the body.
    body =  Helper.ProcessStringField(body, MyWorkflow.ActivityExecutionContext.Activity, null);
     
    MailMessage message = new MailMessage(from, to, subject, body);
    message.IsBodyHtml = true;
    message.Priority = MailPriority.Normal;
     
    // ** Add optional CCs and BCCs
    //message.Bcc.Add("someone@somewhere.com");
    //message.CC.Add("someone@somewhere.com");
     
    // ** Do we need to attach a single file or a list of files attached to a list item?
    if (item.File != null)
    {
        // ** Attach the file itself
        Stream attachmentStream = new MemoryStream(item.File.OpenBinary());
        Attachment attachment = new Attachment(attachmentStream, item.File.Name);
        message.Attachments.Add(attachment);
    }
    else
    {
        // ** Attach all files that are part of the list item
        foreach (string fileName in item.Attachments)
        {
            SPFile file = item.ParentList.ParentWeb.GetFile(item.Attachments.UrlPrefix + fileName);
            Stream attachmentStream = new MemoryStream(file.OpenBinary());
            Attachment attachment = new Attachment(attachmentStream, file.Name);
            message.Attachments.Add(attachment);
        }
    }
     
    // ** Send the email
    SmtpClient client = new SmtpClient(smtpServer);
    client.Send(message);
          
  12. Close the Workflow Designer and add an item to your list or library to trigger the workflow.
        emailAttachments

 
The code in this example is a great starting point for making further customisations. You may want to change which parameters are passed in or load the attachments from a different item (See this Knowledge Base Article) or even using an HTTP Request. The solution presented in this post also works very well when combined with one of our other postings: Automatically convert files to PDF using an e-mail enabled SharePoint Document Library

.




Labels: , , , , ,

Create Shortened (‘TinyURL’) links from your SharePoint Workflow

Posted at: 17:24 on 28 December 2009 by Muhimbi

MushBoxWhen we released the Muhimbi URL Shortener for SharePoint, we knew that even though it was a great product, we couldn’t please everyone. For example, all generated Short URLs point to list items’ Form View, from where the item can be edited, deleted or opened in an external application. This behaviour is by design, but some customers prefer to have the short URL point directly to the file, e.g. a Generated PDF.

In this article we describe how to generate a short URL from a SharePoint Designer workflow with full control over which view the URL points to using both MuSH and the Workflow Power Pack.

A quick introduction for those not familiar with the product: The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.

The solution presented below creates a short URL for the current item the workflow is acting on. An optional value can be specified in Parameter 1 to control if the generated URL points to the file or display form using the ‘displayform’ and  ‘file’ values.

Create the workflow as follows:

  1. Download and install the Muhimbi Workflow Power Pack for SharePoint.
     
  2. Download and install the Muhimbi URL Shortener for SharePoint.
     
  3. We need to be able to access functionality in the Muhimbi.SharePoint.URLShortener assembly. Add this reference to the relevant Web Application using the Workflow Power Pack Central Administration screens as described in the Administration Guide.
      
  4. Make sure you have the appropriate privileges to create workflows on a site collection.
     
  5. Create a new workflow using SharePoint Designer.
     
  6. On the Workflow definition screen associate the workflow with the list or library of your choice, tick the box next to ‘Automatically start this workflow when a new item is created’ and proceed to the next screen.
     
  7. Click the Actions button and insert the Execute Custom Code action.
     
  8. Click this variable and create a new Workflow Variable named shortURL using string as the type.
     
  9. Click parameter 1 and enter file or displayform or leave it empty to default to displayform.
     
  10. Insert the following C# based code by clicking this code.
     
    using Muhimbi.SharePoint.URLShortener;
     
    // ** Where do we want the shortened URL to point to?
    string target = MyWorkflow.Parameter1 == null ? "displayform" : (String) MyWorkflow.Parameter1;
     
    string sourceURL = null;
     
    // ** Point the short URL to the file or the file's info form
    switch (target.ToLower())
    {
        case "file":
            sourceURL = String.Format("{0}/{1}", MyWorkflow.Web.Url, MyWorkflow.Item.Url);
            break;
        case "displayform":
            sourceURL = String.Format("{0}/{1}?ID={2}",
                                        MyWorkflow.Web.Url,
                                        MyWorkflow.List.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url,
                                        MyWorkflow.Item.ID);
            break;
        default:
            throw new ArgumentException("Unknown target specified, use 'file' or 'displayform'.");
    }
     
    // ** Add a description to the short URL, amend if necessary
    string description = "Generated from workflow";
     
    // ** Carry out the shortening process
    ShortenedURL shortURL = Muhimbi.SharePoint.URLShortener.URLShortener.CreateURLViaWebService(
                                    MyWorkflow.Site.WebApplication.Id, sourceURL, description);
     
    // ** Return the fully qualified URL back to the workflow
    MyWorkflow.ReturnValue = shortURL.GetFullyQualifiedShortURL();
      
  11. Click the Actions button, select Log to History List, click this message, set the Source to Workflow Data and the Field to shortURL.
     
  12. Close the Workflow Designer and add an item to your list or library to trigger the workflow.
     
  13. Once the workflow has finished, click the Completed link to see the generated short URL.
      generateShortURL 

Naturally this is just a simple example. Under normal circumstances you would do something more useful with the shortURL workflow variable, for example embed it in an email.

.






Labels: , , , , , ,

Synchronise SharePoint Lists (Calendar/Tasks) using the Workflow Power Pack

Posted at: 11:10 on by Muhimbi

mirrorCalendarsWhen I worked in the SharePoint team for a London based financial firm, one of the first user requests that came up was to find an easy way to synchronise team calendar entries with a department level calendar.

There are a number of ways to approach the automatic synchronisation of lists (a Calendar is just a SharePoint list). In this posting I will discuss how to synchronise any source list with a destination list, with the Exception of Document Libraries, using the Muhimbi Workflow Power Pack and a small script.

A quick introduction for those not familiar with the product: The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.

Before developing the script we have to think about our approach as the synchronisation of list items involves both adding new items as well as updating existing ones. In order to be able to update an item we need to somehow track which item in the destination list is a copy of an item in the source list. As SharePoint does not provide an easy way to track this kind of information (The CopySource field is read only, sigh) the script will need to create a new and hidden field on the destination list to track an item’s origin.

The solution presented below allows any two lists to be synchronised in one direction (Source List to Destination List). The name of the Destination List is passed using Parameter 1. Only those fields available in both the source and destination lists will be copied over so there is no need for the two lists to have exactly the same content type. The very first time the script is executed, a new hidden field will be created on the Destination List, so make sure you execute the first run using an account that has the appropriate privileges to add columns to the list.

Create the workflow as follows:

  1. Download and install the Muhimbi Workflow Power Pack for SharePoint.
     
  2. Make sure you have the appropriate privileges to create workflows on a site collection.
     
  3. Create a source and destination list, e.g. Calendar and Destination Calendar.
     
  4. Create a new workflow using SharePoint Designer.
     
  5. On the Workflow definition screen associate the workflow with the source list and tick the two automatic boxes and proceed to the next screen.
     
  6. Click the Actions button and insert the Execute Custom Code action.
     
  7. Click parameter 1 and enter the name of the Destination List.
     
  8. Insert the following C# based code by clicking this code.
     
    SPList sourceList = MyWorkflow.List;
    SPList destinationList = MyWorkflow.Web.Lists[MyWorkflow.Parameter1 as String];
    SPListItem sourceItem = MyWorkflow.Item;
     
    // ** Check if our custom Source field already exists on the destination list
    if (destinationList.Fields.ContainsField("_M_CopySource") == false)
    {
          SPField newField = destinationList.Fields.CreateNewField("Text", "_M_CopySource");
          newField.Hidden = true;
          destinationList.Fields.Add(newField);
    }
     
    // ** Check if the item needs to be copied or updated.
    string camlQuery = "<Where>" +
                       "<Eq><FieldRef Name='_M_CopySource'/><Value Type='Text'>{0}</Value></Eq>" +
                       "</Where>";
    camlQuery = string.Format(camlQuery, sourceItem["FileRef"]);
    SPQuery query = new SPQuery();
    query.Query = camlQuery;
    query.RowLimit = 1;
     
    // ** Query the list
    SPListItemCollection items = destinationList.GetItems(query);
    SPListItem newItem = null;
    if (items.Count == 0)
        newItem = destinationList.Items.Add();
    else
        newItem = items[0];
     
    // ** Copy the fields
    foreach(SPField field in sourceItem.Fields)
    {
        if (newItem.Fields.ContainsField(field.InternalName) == true && 
            field.ReadOnlyField == false && field.InternalName != "Attachments")
        {
           newItem[field.InternalName] = sourceItem[field.InternalName];
        }
    }
     
    // ** Delete any existing attachments in the target item
    for (int i = newItem.Attachments.Count; i > 0; i-- )
    {
        newItem.Attachments.Delete(newItem.Attachments[i-1]);
    }
     
    // ** Copy any attachments
    foreach (string fileName in sourceItem.Attachments)
    {
        SPFile file = sourceItem.ParentList.ParentWeb.GetFile(sourceItem.Attachments.UrlPrefix + 
                                                              fileName);
        byte[] imageData = file.OpenBinary();
        newItem.Attachments.Add(fileName, imageData);
    }
     
    // ** Remember where the original was copied from so we can update it in the future
    newItem["_M_CopySource"] = sourceItem["FileRef"];
     
    newItem.Update();
      
  9. Close the Workflow Designer and add an item to the source list to trigger the workflow.
     
  10. Once the workflow has finished, open the destination list and verify the source item has been copied over.
     
      mirrorCalendarEntries 

As mentioned previously, it should be possible to use this script on any list with the exception of Document Libraries. Synchronising Document Libraries can be done using a similar approach, but copying an item works slightly different.

The script in this post assumes the destination list lives in the same site as the source list. This, however, can be easily changed by referencing the desired list in line 2 of the code.

You may want to add a condition to only synchronise list items that match certain criteria (e.g. status = completed).

.






Labels: , , , ,

Automatically mirror / sync a SharePoint folder structure using the Workflow Power Pack

Posted at: 09:05 on 23 December 2009 by Muhimbi

Mirror Quite a few of our customers use a SharePoint Designer workflow to automatically create PDF files whenever a document is created or modified in a SharePoint Document Library. Some of these customers want to use the same directory structure for the source files as well as the converted files.

The problem that we’ll address in this posting is how to automatically synchronise the two directory structures using the Muhimbi Workflow Power Pack and a small script.

A quick introduction for those not familiar with the product: The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.

The script we’ll develop is executed every time a file is created anywhere in a document library. The file’s path will be extracted and replicated in the destination Document Library. Ideally we would only like to trigger this workflow whenever a new folder is created, but using SharePoint designer it is not possible to trigger workflows for folders.

At the end of the custom activity the destination path is returned to the workflow, from where it can be used for further processing.

Create the workflow as follows:

  1. Download and install the Muhimbi Workflow Power Pack for SharePoint.
     
  2. Make sure you have the appropriate privileges to create workflows on a site collection.
     
  3. Create a source and destination Document Library. Alternatively use the same Document Library for the source and the destination, just create a new folder in the root to mirror the folder structure to.
     
  4. Create a new workflow using SharePoint Designer.
     
  5. On the Workflow definition screen associate the workflow with the Source Document Library  and tick the box next to “Automatically start this workflow when a new item is created” and proceed to the next screen.
     
  6. Click the Actions button and insert the Execute Custom Code action.
     
  7. Click this variable and create a new Workflow Variable named destinationPath using string as the type.
     
  8. Insert the following C# based code by clicking this code.
     
    SPList sourceList = MyWorkflow.List;
     
    // ** The destination Document Library (optionally point it to a different site collection)
    SPList destinationList = MyWorkflow.Web.Lists["Some Destination DocLib"];
     
    // ** If the destination directory structure should not start in the root directory, enter
    // ** the folder path to the root destination folder here
    string rootFolderName = "";
    string destinationListURL = destinationList.RootFolder.ServerRelativeUrl + rootFolderName;
     
    // ** Generate a list of folders for the current item
    SPListItem sourceFile = MyWorkflow.Item;
    Stack<string> folders = new Stack<string>();
    SPFolder folder = sourceFile.File.ParentFolder;
    while (folder.Name != string.Empty)
    {
        folders.Push(folder.Name);
        folder = folder.ParentFolder;
    }
     
    // ** discard the first as it is the name of the Doc lib itself
    folders.Pop();
     
    // ** Iterate through the list of folders in reverse
    while(folders.Count != 0)
    {
        string folderToCreate = folders.Pop();
        // ** Check if the folder already exists, in which case no action is required
        folder = destinationList.ParentWeb.GetFolder(destinationListURL + "/" + folderToCreate);
        if (folder.Exists == false)
        {
            // ** Create the new folder
            SPListItem newFolder = destinationList.Items.Add(destinationListURL, 
                                                SPFileSystemObjectType.Folder, folderToCreate);
            newFolder.Update();
        }
        destinationListURL += "/" + folderToCreate;
    }
     
    MyWorkflow.ReturnValue = destinationListURL;
       
  9. Update the destinationList and rootFolderName variables to match your situation.
     
  10. Click the Actions button, select Log to History List, click this message, set the Source to Workflow Data and the Field to destinationPath.
     
  11. Close the Workflow Designer and add an item somewhere in a nested folder in the source Document Library to trigger the workflow.
     
  12. Once the workflow has finished, click the Completed link to see which path the new folder has been created in. Check that the destination folder(s) have been created by navigating to the Destination Library.
     

mirrorFolders

 

In case you want to use this in combination with Muhimbi’s PDF Converter for SharePoint then you can use the path returned in the destinationPath variable and use it as the destination URL in the Convert To PDF workflow activity. If you want to use it in combination with something else, then use it as you see fit.

Note that a small change may be required to the script, or perhaps as a Custom Code Condition, if the source and destination Document Libraries are the same and files will be written to the location specified in rootFolderName. Otherwise the folder structure inside the destination folder will be mirrored in the destination folder, just at a deeper level.

.






Labels: , , , , ,

SharePoint Workflow Power Pack User Guide - Embedding .net code in a Workflow Action

Posted at: 15:27 on 22 December 2009 by Muhimbi

User-GuideIn part 3 of our series of User Guide related blog postings for the Muhimbi Workflow Power Pack for SharePoint we provide an example of how to embed c# or vb code directly into a SharePoint Designer Workflow Action.

A quick introduction In case you are not familiar with the product: The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.

The following Blog postings are part of this User Guide series:

  1. Language Features: Discusses the script like syntax, the generic workflow action and condition, passing parameters, returning values from a workflow and using the MyWorkflow property.
     
  2. Embedding .net code in a Workflow Condition: Provides a number of examples of how to use the Evaluate Custom Code condition to carry out basic as well as complex conditional tasks.
     
  3. Embedding .net code in a Workflow Action (this article): Contains a number of examples of how to use the Execute Custom Code  to basically carry out any action you can think of in a SharePoint Designer Workflow.
      
  4. Creating Custom Methods: Shows how to create your own methods in your scripts in order to keep the code organised and easy to maintain.


 



SharePoint ships with a number of Workflow Actions to carry out basic tasks such as setting the value of an item or sending an email. However, if you need to do something slightly different that is not supported by any of the stock actions, you need to resort to expensive third party utilities that may match some of your requirements, but probably not all of them.

This section describes how to use the Muhimbi Workflow Power Pack to implement your exact requirements without any limits or dependencies on third party logic. See part 2 of this series for another example that automatically renames a file based on keywords in its name.

 

Reading a SQL Database using a Workflow Action

Although SharePoint can be used to store much of your data, in reality a typical enterprise stores data in all kind of formats and data stores.

In this example we’ll discuss how to use the Muhimbi Workflow Power Pack to retrieve data stored in a SQL Server Database. The example is not particularly practical as we just read a group name out of the SharePoint Content Database, however the same principle can be used for any database.

To create this workflow, carry out the following steps:

  1. We need to be able to access functionality in the System.Data assembly. Add this reference to the relevant Web Application using the Workflow Power Pack Central Administration screens as described in the Administration Guide.
     
  2. Make sure you have the appropriate privileges to create workflows on a site collection.
     
  3. Create a new workflow using SharePoint Designer.
     
  4. On the Workflow definition screen associate the workflow with your list of choice (any list will do), tick the two “automatically start” check boxes and proceed to the next screen.
     
  5. Click the Actions button and insert the Execute Custom Code action.
     
  6. Click this variable and create a new Workflow Variable named groupName using string as the type.
     
  7. Insert the following C# based code by clicking this code.
     
    using System.Data.SqlClient;
     
    // ** Get the connection string for the content DB
    string connString = 
                MyWorkflow.Site.WebApplication.ContentDatabases[0].DatabaseConnectionString;
     
    using(SqlConnection connection = new SqlConnection(connString))
    {
        connection.Open();
     
        // ** Execute the Query
        string sql = "SELECT TOP (1) Title FROM Groups";
        SqlCommand cmd = new SqlCommand(sql, connection);
     
        // ** Store the result in the ReturnValue
        SqlDataReader reader = cmd.ExecuteReader();
        if(reader.Read() == true)
            MyWorkflow.ReturnValue = reader["Title"].ToString();
    }
      
  8. Click the Actions button, select Log to History List, click this message, set the Source to Workflow Data and the Field to groupName.
     
  9. Close the Workflow Designer and add / update an item in the list to trigger the workflow.
     
  10. Once the workflow has finished, click the Completed link to see the output.
     SQLAccess


We are connecting to the content database in this example to make it easier to execute as no new databases or connection strings need to be created.

.






Labels: , , , ,

SharePoint Workflow Power Pack User Guide - Embedding .net code in a Workflow Condition

Posted at: 14:01 on by Muhimbi

User-Guide In part 2 of our series of User Guide related blog postings for the Muhimbi Workflow Power Pack for SharePoint we provide a number of examples of how to embed c# or vb code directly into a SharePoint Designer Workflow Condition.

A quick introduction In case you are not familiar with the product: The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.

The following Blog postings are part of this User Guide series:

  1. Language Features: Discusses the script like syntax, the generic workflow action and condition, passing parameters, returning values from a workflow and using the MyWorkflow property.
     
  2. Embedding .net code in a Workflow Condition (this article): Provides a number of examples of how to use the Evaluate Custom Code condition to carry out basic as well as complex conditional tasks.
     
  3. Embedding .net code in a Workflow Action: Contains a number of examples of how to use the Execute Custom Code  to basically carry out any action you can think of in a SharePoint Designer Workflow.
     
  4. Creating Custom Methods: Shows how to create your own methods in your scripts in order to keep the code organised and easy to maintain.

 


 

SharePoint ships with a number of Workflow Conditions to carry out basic comparisons inside your SharePoint Designer workflows. However, these Conditions are limited in scope and mainly facilitate simple comparisons of workflow variables. 

This post describes how to use the Muhimbi Workflow Power Pack to write your own conditional logic in c# and embed it directly into your SharePoint Designer workflow.

 

Simple Example - File name validation

The following example shows how to check if a document’s file name contains a reference to a highly confidential project (Project Flames). If it does then the document will be renamed automatically.

To create this workflow, carry out the following steps:

  1. Make sure you have access to a Document Library in a site collection and the appropriate privileges to design a workflow on that site collection.
     
  2. Create a new workflow using SharePoint Designer.
     
  3. On the Workflow definition screen associate the workflow with your Document Library, tick the two “automatically start” check boxes and proceed to the next screen.
     
  4. Click the Conditions button and insert the Evaluate Custom Code condition.
     
  5. Insert the following C# based code by clicking code and then the ellipsis (…) button.

    if (MyWorkflow.Item.Name.IndexOf("Flames", StringComparison.CurrentCultureIgnoreCase) >= 0)
    {
        MyWorkflow.ReturnValue = true;
    }
    else
    {
        MyWorkflow.ReturnValue = false;
    }
      
  6. Add the Execute Custom Code Action and insert the following code by clicking this code and pasting the following.
     
    MyWorkflow.Item["Name"] = MyWorkflow.Item.Name.Replace("Flames", "XXXXX");
    MyWorkflow.Item.Update();
      
  7. Close the workflow designer. This will automatically check the syntax of the code embedded in the Custom Action.
     

CheckFileName


The workflow is now ready to be executed. Upload a file named “Project Flames Hostile Takeover.docx” and watch it being renamed automatically.

In this simple example we could have merged the conditional code into the Action. However that would not work with more complex workflows where the Action does not necessarily contain our own custom code.

  


 

Complex example - Check field in InfoPath form

As we have access to the full power of the .net framework, there are very few limitations to how complex the conditional code can be. In the following example we will check the value of a field stored deeply in an XML Document generated by InfoPath. To be more specific, if any of the items in the Expense claim form were filed for a day in the weekend then we will send an email to John the Expenses Manager.

Naturally this conditional logic could have been added inside the validation of the InfoPath form itself. However, that would require access to Tom the InfoPath Expert and Tom is very busy and going on holiday tomorrow. In other words, we will need to be creative.

To create this workflow, carry out the following steps:

  1. We need to be able to access functionality in the System.XML assembly. Add this reference to the relevant Web Application using the Workflow Power Pack Central Administration screens as described in the Administration guide.
     
  2. Make sure you have the appropriate privileges to create Form Libraries and design workflows on a site collection.
     
  3. Open InfoPath, select Customize a Sample followed by Sample - Expense Report.
     
  4. Select Publish from the File menu, save the form anywhere on your local system and complete the wizard to publish it to a new Document Library named Expense Forms in your site collection. There is no need to expose any columns to SharePoint.
     
  5. In the Expense Forms Document Library fill out the form, enter one expense in a weekend and make sure you save it to the Document Library. Don’t submit it as that will go via email.
     
  6. Create a new workflow using SharePoint Designer.
     
  7. On the Workflow definition screen associate the workflow with your new Expense Forms Document Library, tick the two “automatically start” check boxes and proceed to the next screen.
     
  8. Click the Conditions button and insert the Evaluate Custom Code condition.
     
  9. Insert the following C# based code by clicking code and then the ellipsis (…) button.
     
    using System.Xml;
    using System.IO;
     
    // ** Innocent until proven guilty
    MyWorkflow.ReturnValue = false; 
     
    XmlDocument doc = new XmlDocument();
     
    // ** Load the XML Form data out of the current item
    using(Stream s = MyWorkflow.Item.File.OpenBinaryStream())
    {
        doc.Load(s); 
     
        // ** Make sure we can query the namespaces properly.
        XmlNamespaceManager xnsm = new XmlNamespaceManager(doc.NameTable);
        xnsm.AddNamespace("my",
            "http://schemas.microsoft.com/office/infopath/2003/myXSD/2005-10-21T21:12:27");
     
        // ** Select all date nodes and iterate through them
        XmlNodeList list = doc.SelectNodes("//my:date", xnsm);
        foreach (XmlNode node in list) 
        {
            // ** Convert the date string to a real date
            DateTime expenseDate = DateTime.ParseExact(node.InnerText, "yyyy-MM-dd", null);
            if (expenseDate.DayOfWeek == DayOfWeek.Saturday ||
                expenseDate.DayOfWeek == DayOfWeek.Sunday)
            {
                MyWorkflow.ReturnValue = true;
                break;
            }
        }
    }
      
  10. Click the Actions button and select the Send an Email activity and fill in the blanks to make it send an email to the relevant manager.
     
  11. Close the Workflow Designer and re-save the previously saved Expense Form to trigger the workflow.
     

CheckExpense

 

.






Labels: , , , ,

SharePoint Workflow Power Pack User Guide - Language Features

Posted at: 12:06 on by Muhimbi

User-GuideEven though we have only recently released the Muhimbi Workflow Power Pack for SharePoint, we are already getting good feedback from users. Most of the questions we get are already covered in the User Guide, which is why we have decided to republish the user guide as a series of blog postings.

A quick introduction In case you are not familiar with the product: The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.

The following Blog postings are part of this User Guide series:

  1. Language Features (this article): Discusses the script like syntax, the generic workflow action and condition, passing parameters, returning values from a workflow and using the MyWorkflow property.
     
  2. Embedding .net code in a Workflow Condition: Provides a number of examples of how to use the Evaluate Custom Code condition to carry out basic as well as complex conditional tasks.
     
  3. Embedding .net code in a Workflow Action: Contains a number of examples of how to use the Execute Custom Code  to basically carry out any action you can think of in a SharePoint Designer Workflow.
     
  4. Creating Custom Methods: Shows how to create your own methods in your scripts in order to keep the code organised and easy to maintain.

 


 
Script Like Syntax

To allow code to be entered in an easy fashion, without needing to worry about namespaces, class or method names, the WPP takes a script like approach. In essence the person designing the workflow can just enter a single line of code and the WPP will make sure it is wrapped in the appropriate class.

Even though the traditional structure of a typical C# or VB.net file is not used, it is still possible to add shortcuts to namespaces by adding Using (c#) or Imports (VB) statements to the top of the code.

A simple C# code sample looks as follows:

using System.Xml;
 
XmlDocument doc = new XmlDocument();
doc.LoadXml("<Test>Content</Test>");
 
MyWorkflow.ReturnValue = doc.SelectSingleNode("//Test").InnerText;


Note that in order to use System.XML, a reference will need to be added in Central Admin. For details see the Administration Guide





Workflow Action Structure

When adding a new Execute Custom Code action to a workflow you are presented with the following workflow sentence.

ExecuteCustomCodeAction 
The fields are as follows.

Field

Type

Description

this code

Text

The C# or VB.net code to execute.

Language

C# or VB

The language the code is written in.

parameter 1

Object

An optional parameter to pass to the workflow.

parameter 2

Object

Another optional parameter to pass to the workflow.

this variable

Object

An optional workflow parameter to fill with the result.

Enter an optional comment

Text

An optional summary of what the code does.






Workflow Condition structure

When adding an Evaluate Custom Code Condition to a workflow, you are presented with the following.

ExecuteCustomCodeCondition

The fields are as follows.

Field

Type

Description

this code

Text

The C# or VB.net code to execute.

Language

C# or VB

The language the code is written in.

parameter 1

Object

An optional parameter to pass to the condition.

parameter 2

Object

Another optional parameter to pass to the condition.

Enter an optional comment

Text

An optional summary of what the code does.

A condition should always evaluate to either True or False







Passing values and Parameters

Workflow parameters and other values can be passed to the custom code in a number of ways.

Parameter 1 & 2: Use the Parameter 1 & 2 variables to pass a constant, workflow variable or any other kind of Workflow Lookup value. Note that these values are passed as type Object and may need to be cast into the correct data type in your code.

DefineWorkflowLookup

The Parameter1 and Parameter2 values can be accessed from the custom code using the MyWorkflow variable. For details see Using the MyWorkflow section below.

 

Embedding Workflow Lookup variables: Workflow Actions allow lookup variables to be directly embedded into the source code using the Add Lookup button.

LookupVariables

At runtime all lookup variables are placed into the code as text, so please make sure that the generated syntax is still valid.

For example, the following code will not execute correctly:

   String name = [%Variable: firstName%];

As this may result in the following code:

   String name = John;

 

To solve this problem, add quotes around the variable as follows:

   String name = "[%Variable: firstName%]";

Which will result in to following, syntactically correct, code:

   String name = "John";

 

Note that SharePoint does not allow Workflow Lookups to be defined in the code for Custom Conditions as these are handled differently inside the workflow execution engine.







Returning Values

Variables or result values can be returned from your custom code by using the MyWorkflow.ReturnValue property.

Returning values using the return statement is not supported.







Using the MyWorkflow property

When developing workflows you frequently need access to data related to the workflow. To make accessing this data easy you can access the MyWorkflow property from your own code.

MyWorkflowType


MyWorkflow underlying type

The MyWorkflow property has the following fields.

Field

Type

Description

ActivityExecutionContext

ActivityExecutionContext

Represents the execution environment of an Activity. This class selectively exposes workflow runtime capabilities and services to individual activities.

Item

SPListItem

Reference to the item the current workflow is acting on.

List

SPList

Reference to the SharePoint list the current item is located in.

Parameter1

Object

The first, optional, parameter passed in by the workflow.

Parameter2

Object

The second, optional, parameter passed in by the workflow.

Site

SPSite

Reference to the site collection the workflow is located in.

Web

SPWeb

Reference to the site the workflow is located in.

WorkflowContext

WorkflowContext

Reference to the SharePoint workflow Context.

WorkflowInstanceId

Guid

The ID of the current workflow instance.

 

The following code illustrates how to use some of the MyWorkflow fields from your own code.

using System.Workflow.ComponentModel;
using Microsoft.SharePoint.Workflow;
 
// ** Concatenate the strings
String message = String.Format(
    "SPSite: {0} - SPWeb: {1} - SPList: {2} - Item: {3} - P1: {4} - P2: {5} - User: {6}", 
    MyWorkflow.Site.ServerRelativeUrl, MyWorkflow.Web.Title, MyWorkflow.List.Title, 
    MyWorkflow.Item.Title, MyWorkflow.Parameter1, MyWorkflow.Parameter2, 
    MyWorkflow.Web.CurrentUser.LoginName);
 
// ** Log to the workflow history
ISharePointService service = (ISharePointService)
    MyWorkflow.ActivityExecutionContext.GetService(typeof(ISharePointService));
service.LogToHistoryList(MyWorkflow.WorkflowInstanceId, SPWorkflowHistoryEventType.None, 
    0, TimeSpan.MinValue, "", message, message);
 
// ** Pass the message back to the workflow for further processing
MyWorkflow.ReturnValue = message;

 

This is just an example. Rather than logging to the workflow history from your own code, you could simply pass the ReturnValue into a standard SharePoint Designer Log to History List activity.

.






Labels: , , , ,

Embed C# or VB code in SharePoint Designer Workflows using the Workflow Power Pack

Posted at: 10:40 on 21 December 2009 by Muhimbi

One of our customers recently wrote in a support email “If only Microsoft allowed C# code to be used in SharePoint Designer workflows, my life would be so much easier”. Little did this person know that we were in the middle of a development cycle to make his wish come true, just in time for Christmas as well.

The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.

What does this mean for day-to-day SharePoint Designer Workflow development? When Microsoft released SharePoint 2007, they included a basic set of Workflow Actions and Conditions to allow basic comparison of values and simple tasks such as setting a field value or logging to the Workflow History. If you wanted to do anything else your options were limited to expensive third party workflow tools that would hopefully support the functionality you are after, or writing your own solution in Visual Studio.

From as little as $349, the Workflow Power Pack is the last 3rd party workflow solution you’ll ever need. The possibilities are only limited by your own imagination, for example:

  • Carry out tasks by comparing deeply nested XML data inside InfoPath forms.
  • Create Actions that directly access SQL Databases or other common data sources.
  • Make string, date and numerical operations simple (Finally!).
  • Access any SharePoint functionality using the SharePoint Object Model.
  • Send emails and includie attachments.
  • Create folders in the current SharePoint List or any other list regardless of Site Collection.
  • Control security on individual List Items or entire Document Libraries.
  • Invoke web services or any 1st or 3rd party assembly, for example our PDF Converter for SharePoint or URL Shortener for SharePoint.
     

The list goes on and on. Basically, if it exposes a .net interface, you can access it using the Workflow Power Pack.  

Screenshot-SharePointDesigner450x300 Embed C# or VB.net code directly into your SharePoint Designer Workflow

  Key features:

  • Embed C# or VB code in SharePoint Designer Workflow Conditions.
     
  • Embed C# or VB code in SharePoint Designer Workflow Actions.
     
  • Easily access the Workflow context using the MyWorkflow property.
     
  • Easily pass workflow parameters in and out of the custom code.
     
  • Secure the system by controlling access to the assemblies that can be used.
     
  • Comes with comprehensive documentation and access to a professional and responsive support desk.

 

Screenshot-CentralAdmin450 Secure your system by controlling which Assemblies can be accessed

For more information check out the following resources:

  As always, feel free to contact us using Twitter, our Blog, regular email or subscribe to our newsletter.

 

Download your free trial here (1MB).

.

Labels: , , , ,

Converting Office files to PDF Format using a Web Services based interface

Posted at: 16:44 on 02 December 2009 by Muhimbi

Web-Services-Icon One of the key changes introduced with the release of the Muhimbi PDF Converter Server Platform and API 3.0 is the ability to convert typical Office files via a web services based interface. This makes it very simple to convert typical Office files to PDF format from your own .NET, Java or any other web services capable environment.

This post describes the key features of the web services based interface and provides a simple example describing how to convert a document to PDF format. Source code for a more comprehensive demo is available for download as well. Feel free to contact us if you have any questions.
 

Prerequisites

Let’s make sure all prerequisites are in place before we start our tutorial.

  1. Download the PDF Converter Server Platform.
  2. Install it in-line with chapter 2 of the included Administration Guide.

 

Key Features

Key Features of the Muhimbi Server Platform are:

  1. Convert popular document types to PDF or XPS format with near perfect fidelity. At the time of writing support is available for MS-Word, PowerPoint, Excel, InfoPath, Visio and MS-Publisher, but by the time you are reading this additional document formats may have been added.
  2. Scalable architecture that allows multiple conversions to run in parallel.
  3. Runs as a Windows Service. No need to install or configure IIS or other web service frameworks.
  4. Convert password protected documents.
  5. Apply security settings to generated PDF files including encryption, password protection and multiple levels of PDF Security options to prevent users from printing documents or copy a document’s content.
  6. Generate a regular PDF file or a file in PDF/A format.
  7. Generate high resolution PDF Files optimised for printing or normal resolution files optimised for use on screen.
  8. Dynamically refresh a document’s content before generating the PDF. Ideal for merging content from SharePoint custom columns into your PDF file.
  9. Control how to deal with hidden / selected content such as PowerPoint Slides and Excel worksheets. 
     

In addition to the features described above, the MDCS software stack also contains a layer of functionality to control concurrency, request queuing and watchdog services to deal with unresponsive and runaway processes. More detail can be found in the brochure.
 
 

Object Model

Although the Object Model exposed by the web service is easy to understand, the system provides very powerful functionality and fine grained control to specify how the PDF file is generated.

As outlined in the image below, the web service contains 3 methods:

PDF-Converter-Web-Services-Main-Interface

  • Convert: Convert the file in the sourceFile byte array using the specified openOptions and conversionSettings. The generated PDF or XPS file is returned as a byte array as well.
  • GetConfiguration: Retrieve information about which converters are supported and the associated file extensions. Consider calling this service once to retrieve a list of valid file extensions and check if a file is supported before it is submit to the web service. This will prevent a lot of redundant traffic and will increase scalability.
  • GetDiagnostics: Run a diagnostics test that carries out an internal end-to-end test for each supported document type. Call this method to check if the service and all prerequisites have been deployed correctly.

The full object model is available in the following diagram. Click to enlarge it.

PDF-Converter-Web-Services-Class-Diagram

PDF Converter Web Service Class Diagram. Click to enlarge.

 
Simple example code

The following sample shows the minimum steps required to convert a document to PDF format. In our example we are using Visual Studio and C#, but any environment that can invoke web services should be able to access the required functionality. Note that the WSDL can be found at http://localhost:41734/Muhimbi.DocumentConverter.WebService/?wsdl. A Java based example is installed alongside the product and discussed in the User & Developer Guide.

This example does not explicitly set ConversionSettings.Format. As a result the file is converted to the default PDF format. It is possible to convert files to other file formats as well by setting this property to a value of the OutputFormat enumeration. For details see this blog post.

  1. Start a new Visual Studio project and use the project type of your choice. In this example we are using a standard .net 3.0 project of type Windows Forms Application. Name it ‘Simple PDF Converter Sample’.
  2. Add a TextBox and Button control button to the form. Accept the default names of textBox1 and button1.
  3. In the Solution Explorer window, right-click References and select Add Service Reference.
  4. In the Address box enter the WSDL address listed in the introduction of this section. If the MDCS is located on a different machine then substitute localhost with the server’s name.
  5. Accept the default Namespace of ServiceReference1 and click the OK button to generate the proxy classes.
  6. Double click Button1 and replace the content of the entire code file with the following:   
     
using System;
using System.IO;
using System.ServiceModel;
using System.Windows.Forms;
using Simple_PDF_Converter_Sample.ServiceReference1;
 
namespace Simple_PDF_Converter_Sample
{
    public partial class Form1 : Form
    {
        // ** The URL where the Web Service is located. Amend host name if needed.
        string SERVICE_URL = "http://localhost:41734/Muhimbi.DocumentConverter.WebService/";
 
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            DocumentConverterServiceClient client = null;
 
            try
            {
                // ** Determine the source file and read it into a byte array.
                string sourceFileName = textBox1.Text;
                byte[] sourceFile = File.ReadAllBytes(sourceFileName);
 
                // ** Open the service and configure the bindings
                client = OpenService(SERVICE_URL);
 
                //** Set the absolute minimum open options
                OpenOptions openOptions = new OpenOptions();
                openOptions.OriginalFileName = Path.GetFileName(sourceFileName);
                openOptions.FileExtension = Path.GetExtension(sourceFileName);
 
                // ** Set the absolute minimum conversion settings.
                ConversionSettings conversionSettings = new ConversionSettings();
                conversionSettings.Fidelity = ConversionFidelities.Full;
                conversionSettings.Quality = ConversionQuality.OptimizeForPrint;
 
                // ** Carry out the conversion.
                byte[] convFile = client.Convert(sourceFile, openOptions, conversionSettings);
 
                // ** Write the converted file back to the file system with a PDF extension.
                string destinationFileName = Path.GetDirectoryName(sourceFileName) + @"\" +
                                             Path.GetFileNameWithoutExtension(sourceFileName) +
                                             "." + conversionSettings.Format;
                using (FileStream fs = File.Create(destinationFileName))
                {
                    fs.Write(convFile, 0, convFile.Length);
                    fs.Close();
                }
 
                MessageBox.Show("File converted to " + destinationFileName);
            }
            catch (FaultException<WebServiceFaultException> ex)
            {
                MessageBox.Show("FaultException occurred: ExceptionType: " + 
                                 ex.Detail.ExceptionType.ToString());
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
            finally
            {
                CloseService(client);
            }
        }
 
 
        /// <summary>
        /// Configure the Bindings, endpoints and open the service using the specified address.
        /// </summary>
        /// <returns>An instance of the Web Service.</returns>
        public static DocumentConverterServiceClient OpenService(string address)
        {
            DocumentConverterServiceClient client = null;
 
            try
            {
                BasicHttpBinding binding = new BasicHttpBinding();
                // ** Use standard Windows Security.
                binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
                binding.Security.Transport.ClientCredentialType = 
                                                            HttpClientCredentialType.Windows;
                // ** Increase the Timeout to deal with (very) long running requests.
                binding.SendTimeout = TimeSpan.FromMinutes(30);
                binding.ReceiveTimeout = TimeSpan.FromMinutes(30);
                // ** Set the maximum document size to 40MB
                binding.MaxReceivedMessageSize = 50*1024*1024;
                binding.ReaderQuotas.MaxArrayLength = 50 * 1024 * 1024;
                binding.ReaderQuotas.MaxStringContentLength = 50 * 1024 * 1024;
 
                // ** Specify an identity (any identity) in order to get it past .net3.5 sp1
                EndpointIdentity epi = EndpointIdentity.CreateUpnIdentity("unknown");
                EndpointAddress epa = new EndpointAddress(new Uri(address), epi);
 
                client = new DocumentConverterServiceClient(binding, epa);
 
                client.Open();
 
                return client;
            }
            catch (Exception)
            {
                CloseService(client);
                throw;
            }
        }
 
        /// <summary>
        /// Check if the client is open and then close it.
        /// </summary>
        /// <param name="client">The client to close</param>
        public static void CloseService(DocumentConverterServiceClient client)
        {
            if (client != null && client.State == CommunicationState.Opened)
                client.Close();
        }
 
    }
}

Providing the project and all controls are named as per the steps above, it should compile without errors. Run it, enter the full path to the source file, e.g. an MS-Word document, and click the button to start the conversion process. The conversion may take a few second depending on the complexity of the document.

Note that In this example we are programmatically configuring the WCF Bindings and End Points. If you wish you can use a declarative approach using the config file.

Download the source code including a compiled binary.
 
 

Complex sample code

In order to carry out internal testing we have developed an application that can be used to control each end every function exposed by the web services. The full source code as well as a compiled binary can be downloaded below.

Note that although the test harness works well and can be used to batch convert a large number of documents, this is not commercial grade code. Use at your own risk.

Complex-Sample

Download the source code including a compiled binary.
 
 

Final notes

If you wish to access the PDF Converter from your own custom SharePoint code, you may want to consider using our high level Wrapper methods. If you are not using the wrapper methods then please make sure you are invoking the web service from a user who has privileges to do so. By wrapping the code in SPSecurity.RunWithElevatedPrivileges you will automatically connect using an account in the WSS_WPG windows group, which has access by default.

.

Labels: , , , ,

Subscribe to News feed