March 2004 - Posts

[Via Patrick Tisseghem] I would like to welcome Yves Kerwyn to the blogosphere, another Belgian blogger! I'm expecting a lot of cool SharePoint and FrontPage posts, so subscribe here.

I get quite a lot of questions about creating alerts in SharePoint: many site administrators want to be able to create alerts on behalf of other users. At this point, this not possible in SharePoint, but the SharePoint object model allows you to create alerts for any user. As an example I've create the UserAlerts webpart:

This webpart has following features:

  • Displays a list of all alerts for a specific SharePoint list.
  • Allows deletion of a specific alert.
  • Allows deletion of all alers for a specific SharePoint list.
  • Allows creation of an alert for a specific user.
  • Allows creation of an alert for all users.
  • Select type of the created alerts.

Before you use this webpart in a production site, please keep in mind following limitations/remarks:

  • The webpart can only be used by SharePoint Site Administrators.
  • If you have many SharePoint users and many SharePoint alerts, performance of the webpart could be quite bad due to the way the alerts are retrieved from SharePoint.

As always, you can download a MSI Package for easy installation as well as the full source code. Please feel free to leave your comments!

Darell Norton has a nice BizTalk Overview. It seems that the BizTalk community is gaining some momentum; articles start to appear and people are blogging about this product. Although the limited documentation still leaves a big gap, let's hope the updated docs are released as planned on the 2nd of April!

BizTalk Server 2004 has some basic built-in functionalities for dealing with stuff that can go wrong, for example retries on an adapter for which you can specify the retry delay and retry count. But in many cases when you’re working with custom components, mappers, etc. on an Orchestration, you want to be able to catch exceptions. BizTalk has a shape which you can use for this, but before we’re going to take a look at it, let’s think about what we want to do if something goes wrong. Suppose you want to create a message containing information about the exception and send that message to a send port, additionally we want to include the message that caused the exception. And of course it would be nice if all of this stuff would be reusable in other BizTalk projects.

First of all we need to define the schema of the message we want to send: create a new empty BizTalk project (ExceptionMessage) and add a new Schema to it (name it Exception for example). Let’s add three nodes that will contain some metadata: Description (xs:string), TimeStamp (xs:dateTime) and Orchestration (xs:string). Finally we need to add a node that will contain the originating message: add a Child Record node under the root node and name it Message. Remember we want this solution to be reusable, so we can’t define at this point which schema the Message node will have; we want it to accept every schema. This can be accomplished by inserting a Any Element node under the Message node and setting the Process Contents property to Skip (no validation) and the Namespace property to ##any (any namespace). The BizTalk project containing our schema needs to get deployed, so we need to give it a strong name and then we can deploy it.

In fact now we can already use the Exception schema in an Orchestration, but let’s make our life a little more easier by creating some helper library that will let us easily create new Exception messages. This library is just an ordinary Class Library project which we will use from an Orchestration. So in VS.NET create a new Class Library project (ExceptionUtilities for example) and add following two classes to it:
using System;
using System.Xml;
namespace ExceptionUtilities
{
 [Serializable]
 public class ExceptionInfo
 {
  string _description;
  DateTime _timeStamp;
  string _orchestration;
  string _message;

  public ExceptionInfo()
  { this.TimeStamp = DateTime.Now; }

  public string Description
  {
   get {return _description;}
   set {_description = value;}
  }
  public DateTime TimeStamp
  {
   get {return _timeStamp;}
   set {_timeStamp = value;}
  }
  public string Orchestration
  {
   get {return _orchestration;}
   set {_orchestration = value;}
  }
  public string Message
  {
   get {return _message;}
   set {_message = value;}
  }
 }

 public class ExceptionHelper
 {
  public static void SetExceptionMessage
   (ExceptionInfo exInfo, XmlDocument msg)
  {
   exInfo.Message = msg.OuterXml;
  }

  public static System.Xml.XmlDocument
   GetMessage(ExceptionInfo exInfo)
  {
   string xml = string.Format(
    "<ns0:Exception xmlns:ns0='http://ExceptionMessage.Exception'>" +
    "  <Description>{0}</Description>" +
    "  <TimeStamp>{1}</TimeStamp>" +
    "  <Orchestration>{2}</Orchestration>" +
    "  <Message>{3}</Message>" +
    "</ns0:Exception>"
    , exInfo.Description, XmlConvert.ToString(exInfo.TimeStamp),
    exInfo.Orchestration, exInfo.Message );

   XmlDocument doc = new XmlDocument();
   doc.LoadXml(xml);
   return doc;
  }
 }
}

The ExceptionInfo class is just data class, containing only the four properties that correspond with the four nodes of the Exception schema. Only one important thing: the class is decorated with the Serializable attribute. This is important, it allows BizTalk to serialize the contents of an instance of that class to disk if something really bad happens (e.g. immediate shutdown) when this class is used in an Orchestration. You don’t have to use the Serializable attribute, you could also set the transaction type property of the Orchestration to Atomic but this could have consequences later on. The ExceptionHelper class is the class that will do the work for us. The SetExceptionMessage will add the contents of an XmlDocument, which will be a message in a BizTalk Orchestration in our case, to an ExceptionInfo instance. (You could ask yourself why this method isn’t implemented on the ExceptionInfo class. The short answer is: the XmlDocument class is not serializable.) The GetMessage method finally will construct an XmlDocument based on an ExceptionInfo instance. Notice that both methods are declared a static, so we don't need an instance of the ExceptionHelper class, so we don't need to bother about serialization issues. The ExceptionUtilities library is finished, give it a strong name and deploy it to the GAC so BizTalk will be able to use it.

You can use the ExceptionUtilities library in an Orchestration like this:

  • A simple message is received that contains two integer values.
  • The Scope shape contains an Expression shape that divided these two values.
  • The Catch block catches the DivideByZeroException, constructs a decent message and sends that message to a port.

To create a Catch block for a Scope shape, just right click on the icon of a Scope shape on your Orchestration and choose New Exception Handler. In the properties you can specify what type of exception to catch. The “Construct ExceptionInfo” Expression shape contains following expression:
ExceptionInfo.Description = "Something went wrong...";
ExceptionInfo.Orchestration = "TestExceptions.odx";
ExceptionUtilities.ExceptionHelper.SetExceptionMessage
     (ExceptionInfo, ValuesMessage);
The Assign shape in the “Construct exMessage” shape contains this expression:
exMessage =
 ExceptionUtilities.ExceptionHelper.GetMessage(ExceptionInfo);

If we feed the Orchestration a message that would result in a DivideByZeroException, a descriptive message is constructed that contains all the information we need to solve the problem.

Owen Allen (MS E-Business Technologies) has updated my list of File Send Handler Macros, thanks Owen!

Macro name

Substitute value

%datetime%

Coordinated Universal Time (UTC) date time in the format YYYY-MM-DDThhmmss (for example, 1997-07-12T103508).

%datetime_bts2000%

UTC date time in the format YYYYMMDDhhmmsss, where sss means seconds and milliseconds (for example, 199707121035234 means 1997/07/12, 10:35:23 and 400 milliseconds).

%datetime.tz%

Local date time plus time zone from GMT in the format YYYY-MM-DDThhmmssTZD, (for example, 1997-07-12T103508+800).

%DestinationParty%

Name of the destination party. The value comes from message the context property BTS.DestinationParty.

%DestinationPartyID%

Identifier of the destination party (GUID). The value comes from the message context property BTS.DestinationPartyID.

%DestinationPartyQualifier%

Qualifier of the destination party. The value comes from the message context property BTS.DestinationPartyQualifier.

%MessageID%

Globally unique identifier (GUID) of the message in BizTalk Server. The value comes directly from the message context property BTS.MessageID.

%SourceFileName%

Name of the file from where the File adapter read the message. The file name includes extension and excludes the file path, for example, foo.xml. When substituting this property, the File adapter extracts the file name from the absolute file path stored in the FILE.ReceivedFileName context property. If the context property does not have a value, for example, if message was received on an adapter other than File adapter, then the macro will not be substituted and will remain in the file name as is (for example, C:\Drop\%SourceFileName%).

%SourceParty%

Name of the source party from which the File adapter received the message.

%SourcePartyID%

Identifier of the source party (GUID). The value comes from the message context property BTS.SourcePartyID.

%SourcePartyQualifier%

Qualifier of the source party from which the File adapter received the message.

%time%

UTC time in the format hhmmss.

%time.tz%

Local time plus time zone from GMT in the format hhmmssTZD (for example, 124525+530).

A Basic Introduction to Messaging with Microsoft BizTalk Server 2004
By: Christof Claessens

Summary:
BizTalk Server 2004 has just been released and a lot of people are planning to hit the road with it. While BizTalk Server 2004 has lot to offer, this article focuses on the core workings of this brand new server, more in particular: messaging.

Christof is a (Belgian) guy who really knows a lot (and I really mean A LOT) of the new BizTalk 2004 (he also knows how to party ;-). He told me he will have a blog on weblogs.asp.net soon, I'm looking forward to that Christof!

Since the Service Pack 1 Preview of InfoPath it is really simple to fill for example a drop-down list box on a InfoPath form with data that is stored in a SharePoint list. But it’s not quite straight forward to fill that drop-down list box with all the users of a SharePoint site. Although this is quite easy to accomplish in a custom SharePoint list by creating a Lookup column and indicating that you want to get User Information. But (almost) nothing is impossible with InfoPath, so let’s give it a try!

First of all: how can we get a hold of a list with all the users of a specific SharePoint site? Well it happens to be that SharePoint exposes quite a lot of functionality through a web services layer; to be more specific: we can use the Users and Groups Service. In this service are several methods that you can use to retrieve all kinds of information about users and groups, we’ll use the GetUserCollectionFromSite method. So far so good, we know where to get our data, now let’s create the InfoPath form. The solution I’ll describe here will be using some custom code which can be written in script or in .NET code. This time, let’s make use of the InfoPath 2003 Toolkit for Visual Studio.NET so we can write .NET code.

Thus let’s fire up Visual Studio .NET and choose to create a new project based on the InfoPath Form Template. Drop a drop-down list box on your InfoPath form and open the Properties window. In the “List box entries” section choose to “Look up values in a data connection to a database, Web service, file or SharePoint library or list”. At this point the InfoPath form doesn’t have any data connections, hence click the Add button to create a new one. We want to get our data from the GetUserCollectionFromSite web method, so select the “Web service” option in the wizard. For the WSDL location use a URL formatted as follows: http://Server_Name/[Site_Name/]_vti_bin/UserGroup.asmx?wsdl (for example: http://myServer/SharePoint/_vti_bin/UserGroup.asmx?wsdl). Next the wizard will show you a list containing all the web methods available on the web service, select the GetUserCollectionFromSite method. Finally you can give the data connection a name, let’s call it “Users”. When you click the Select XPath button (right to the Entries text box), the troubles begin… As you can see on the screenshot, the web service return value does not contain a repeating group or field, which is necessary to be able to fill a list.

Why is that? Well if we take a look at the GetUserCollectionFromSite documentation we can see that the return value is of the System.Xml.XmlNode type. That means that you’ll receive XML of which the WSDL doesn’t describe how it will look like, so InfoPath can’t show your either. Although the documentation shows an example of how the XML will look like, it’s quite a bummer. The problem would be solved if you could type the XPath value in the Entries text box, but unfortunately this text box is read-only. From now on, there are several possible solutions:

  • Manually edit the XSLT of the InfoPath form and fill in the correct XPath value.
  • Create a wrapper web service that exposes the GetUserCollectionFromSite information strongly typed.
  • Use the little trick I’ll show you in a moment. :-)

The third solution will make use of a dummy data connection that has the same structure as the return value of the web method. This will give you the advantage of using InfoPath functionality to select the correct repeating group. Afterwards the contents of that dummy data connection are replaced by return value of the web method. So let’s work further on our InfoPath form but first download this XML file to your harddisk; it contains a fictitious return value of the GetUserCollectionFromSite web method. Click the Add (data connection) button to add a new one data connection: receive data from an XML document (point to the file you’ve just downloaded) and name it “DummyUsers”. Un-check the “Automatically retrieve data when the form is opened” check box and choose No if InfoPath suggest to file to your form. Then open the Properties window of the drop-down list box control again, select the “Look up values in a data connection to a database, Web service, file or SharePoint library or list” option, and choose to DummyUsers data connection. Then if you choose the “Select XPath” button you’ll see that you can navigate to a repeating group! Also in the Properties window, you need to specify the Value and “Display name” field, for example choose the LoginName field for both ones.

We’re not finished yet, we need to replace the data of the DummyUsers data connection with the data of the Users data connection (which is automatically fetched through the web service). We must do this in the OnLoad event of the InfoPath form, so in the Tools/Programming menu click the “On Load Event” menu item. Visual Studio.NET will pop up, and a the OnLoad method is created for you. You need only to add one line:

// The following function handler is created by Microsoft Office InfoPath. Do not
// modify the type or number of arguments.
[InfoPathEventHandler(EventType=InfoPathEventType.OnLoad)]
public void OnLoad(DocReturnEvent e)
{
            // Write your code here.
            thisXDocument.DataObjects["DummyUsers"].DOM.loadXML(
                        thisXDocument.DataObjects["Users"].DOM.xml);
}

That’s it! If you preview the InfoPath form, first the data of the Users data connection is fetched automatically. Then the OnLoad event is triggered, so we replace the data of the DummyUser data connection, and finally the form is showed and the drop-down list box contains all the login names of the users of the SharePoint site. You can use this little trick also for fetching other data through the SharePoint web services. If you want to easily create dummy XML documents you can use the WebServiceStudio 2.0 that can construct SOAP request messages.

The last few weeks I’ve been pretty busy with BizTalk 2004 (those of you who are in my Messenger buddy list can whiteness that) and I must say it’s for a full 100% a product of the new Microsoft generation. What I mean by that is that everything is so extensible... You need to do something special in a receive pipeline? Just create the component in .NET and drop it in your receive pipeline. You need custom treatment of a message? Just create the component and drop it in an orchestration. And none of these customizations are rocket science, everyone with some decent knowledge of .NET can do it! To prove it, let’s create a custom Functoid that allows you to encrypt/decrypt values in your messages.

First of all: what the heck is a Functoid?

 This is the definition from the glossary: An executable module that performs a specific calculation or data manipulation, and that is used graphically when constructing BizTalk Server maps to provide the basis for richer transformations than what is provided by XSLT on its own. (Btw: a updated set of the BizTalk documentation is coming on the second of April!). So it’s an tiny little component that you can use in the BizTalk Mapper when you transform a message to another message. There are quite a lot of Functoids already available out-of-the box in BizTalk 2004, going from simple string functions to more complex lookup’s in database tables. There are even Functoids in which you can type C# or VB.NET code that will be executed dynamically. Therefore the need for custom Functoids is in fact not very high, only in some cases where you need specific functionality repeatedly or you need the higher performance of compiled code, a custom Functoid can come in handy.

Imagine the following situation: you’re designing the EAI layer of a company that has to deal with credit card information of it’s customers. Probably you’re a little worried because you’ll be sending XML messages containing sensitive data to all sort of legacy systems. Of course you can use secure communication channels like HTTPS, but your manager really wants to have an extra layer of encryption for specific elements of your XML messages. So in the messages you’ll be receiving, the CreditCardNumber field will be encrypted, thus you’ll have to decrypt that field before you send it further. Let’s create some Functoids that can help us with the decryption and encryption!

Important note: the following example is meant to explain how to create a custom Functoid, it is not meant to illustrate a security best-practise!

You can create Functoids in a Class Library project in Visual Studio.NET, so let’s create one, and name it “SecurityFunctoids” for example. We’ll need a reference to the Microsoft.BizTalk.BaseFunctoids.dll assembly that is located in the Developer Tools directory of your BizTalk installtion (probably X:\Program Files\Microsoft BizTalk Server 2004\Developer Tools). This assembly contains the base Functoid class from which we’ll inherit. Since the assembly we’re creating will need to be deployed to the GAC, we’ll need to give it a strong name, so put the location of your public/private key pair in the AssemblyKeyFile attribute in the AssemblyInfo.cs file. Also in the AssemblyInfo.cs file add the Guid attribute, generate a new Guid for your Functoid (for VS.NET interoperability) and give you’re assembly a meaningful title:
[assembly: System.Runtime.InteropServices.Guid("2E345003-E2B0-47c3-89CB-3AC93A824064")]
[assembly: AssemblyTitle("SecurityFunctoids")]

Next we’ll need to add an Assembly Resource File to the project (Resources.resx for example) that will contain for each Functoid following items:

  • Name
  • Description
  • ToolTip
  • Bitmap (will be showed in the ToolBox)

I prefer to use the Resourcer tool to create resource files, it allows you to easily add various kinds of data (like text, bitmaps, …) to your resource file.

Now we’re ready to write some code! Add a new class to your project: DecryptionFunctoid, and use following code:
using System;
using Microsoft.BizTalk.BaseFunctoids;
using System.Reflection;
namespace SecurityFunctoids
{
            public class DecryptionFunctoid: BaseFunctoid
            {
                        public DecryptionFunctoid()
                        {
                                   this.ID = 6002;
                                   SetupResourceAssembly("SecurityFunctoids.Resources", 
                                               Assembly.GetExecutingAssembly());
                                   SetName("ENCRYPTION_NAME");
                                   SetTooltip("ENCRYPTION_TOOLTIP");
                                   SetDescription("ENCRYPTION__DESCRIPTION");
                                   SetBitmap("ENCRYPTION__BITMAP");
                                   this.SetMinParams(2);
                                   this.SetMaxParams(2);
                                   SetExternalFunctionName(GetType().Assembly.FullName, 
                                               "SecurityFunctoids.EncryptionFunctoid", "DecryptString");
                                   this.Category = FunctoidCategory.String;
                                   this.OutputConnectionType = ConnectionType.AllExceptRecord;
                                   // Parameter 1
                                   AddInputConnectionType(ConnectionType.AllExceptRecord);
                                   // Paramter 2
                                   AddInputConnectionType(ConnectionType.AllExceptRecord); 
                        }
                        public string DecryptString(string val1, string val2)
                        {
                                   SymmetricEncryption enc = new SymmetricEncryption();
                                   return enc.Decrypt(val1, val2);
                        }
            }
}

As you can see, the class inherits from the BaseFunctoid. In the constructor there are a number of things set:

  • Unique ID of the Functoid (start from 6000 for your own Functoids).
  • Name, Tooltip, Description and Bitmap resource strings.
  • The maximum and minimum number of parameters your Functoid will accept. In our case both are set to two. The first parameter will be the value to decrypt, the second parameter will be the key to use.
  • The SetExternalFunctionName determines which function of the class will be used when the Functoid is invoked, in our case the DecryptString function.
  • The category is specified by using the Category property.
  • The parameters of the Functoid are configured.

Then the function that will do the work is written next: DecryptString. I’ve used a helper library for encryption and decryption of string values, so I won’t go into detail about encryption/decryption functionality. Finally build your project so we can use it in BizTalk. The deployment of a Functoid consist of three steps:

  • Deploy it to the GAC
  • Copy it to the X:\Program Files\Microsoft BizTalk Server 2004\Developer Tools\Mapper Extensions directory (So Visual Studio.NET can use it)
  • Add it to the ToolBox in Visual Studio.NET (right click on the ToolBox, choose Add/Remove items, click the Functoids tab and browse to the SecurityFunctoids.dll file and make sure it’s checked.

If you’ve deployed your Functoid correctly you should be able to see it in the ToolBox:

To use the new Functoid you can create a similar orchestration as showed below:

The DecryptMessage Transform shape should contain String Decryption Functoid:

For samples of more advanced Functoids check out Scott Woodgate’s blog post.

UPDATED HERE!!

Mike Walsh has posted his comments on my webparts. Mike's right; I only provided installation instructions that could be understood by developers (since the Dev&IT-Pro Days everybody knows that developers are from another planet!). So thanks to Patrick's excellent post on deploying webparts, I had no excuses left to not provide an easy installation. :-)

Now I only have to create nice site for my webparts ... If I only could find some time ...

The InfoPath Service Pack1 adds some nice features to InfoPath, there a few that I really like. One of them is the ability to use a SharePoint list as a data source. For example if you have a contact list in SharePoint containing information about your customers, you can easily create a drop-down list box on an InfoPath form, that automatically gets filled with a list of your customers. That’s already nice, but in some cases you may want to fill other fields on your form, based on the selected item of your drop-down list box. For example: you want to display the address of the selected customer on your InfoPath form. This involves a little bit of plumbing code which can be done either in .NET code (by using the InfoPath 2003 Toolkit for Visual Studio.NET) or in Java/VB-script. So because not everyone lives in a managed world today, let’s do it in good old VB-script! (sometime I’m a little bit old fashioned…)

First of all create a new InfoPath Form. Then put a drop-down list box on your form, double-click on it so we can set the properties. In the “List box entries” section of the properties window, select “Look up values in a data connection to a database, Web Service, file or SharePoint library or list”. When you click the Add-button a wizard shows up which allows you to easily choose a data source. First select of course “SharePoint library or list”, then enter the URL of the SharePoint site that contains the list you want to use. The wizard will retrieve all the lists and libraries on your site, in our example we’ll choose the Contacts list. In the next window you can choose which fields of that list you want to use, make sure you’ve selected the Address and City fields and finish the wizard. Further on the properties window you can choose the XPath expression for the Entries property. By clicking the button right of the Entries textbox, you can easily navigate to the Contacts node. For the Value and Display name properties you can choose something meaningful, for example the Company field. Now if you preview this form, the drop-down list box already contains a list of your customers!

What we want to do is after the user has selected a customer from the drop-down list box, is display the address and city for that user. Add two text boxes to the form and name them Address and City for example. Double-click again on the drop-down list box (so the Properties shows up again) and click the “Data Validation…” button. On the button of the Data Validation window, select the OnAfterChange event and click the Edit button. Microsoft Script Editor shows up and there’s automatically an event handler generated for the OnAfterChange event of the drop-down list box. Now enter following code after the last remark lines:

' Get to the DOM
Dim doc
Set doc = XDocument.DataObjects("Contacts").DOM

' Add the namespaces for the SelectSingleNodes
doc.setProperty _
   "SelectionNamespaces","xmlns:dfs=""http://schemas.microsoft.com/office/infopath/2003/dataFormSolution"" " & _
   "xmlns:dsf=""http://schemas.microsoft.com/office/infopath/2003/dataFormSolution"" "

' Get the selected customer text
Dim selectedCustomer
selectedCustomer = XDocument.DOM.selectSingleNode("/my:myFields/my:Customer").text

' Get the selected node
Dim selectedNode
Set selectedNode = _
            doc.selectSingleNode("/dfs:myFields/dfs:dataFields/dsf:Contacts[@Company='" & _
            selectedCustomer & "']")

' Set the textbox texts
XDocument.DOM.selectSingleNode("/my:myFields/my:Address").text = _
            selectedNode.attributes.getNamedItem("Address").text

XDocument.DOM.selectSingleNode("/my:myFields/my:City").text = _
            selectedNode.attributes.getNamedItem("City").text

Close the Microsoft Script Editor or save your script manually and preview the InfoPath form. When you select a Customer from the list, automatically the corresponding values for the Address and the City fields will be showed!

More Posts Next page »