April 2004 - Posts

Another day off, so another few hours to spend on cool coding stuff! In my last post I introduced you my new “pet project”: a generic SharePoint Document Library event handler that can be used for simple workflow and approval scenarios. Thanks to all the people who contacted me! Gerry posted a long reply in which he made the trade-off of developing your own workflow solution or buying a commercial product, and he would go for a commercial product. I appreciate Gerry’s opinion but let me set some things straight: it’s not my intention to compete in any way with commercial products like K2.NET, Teamplate or even BizTalk (although that would be cool ;-). They offer a lot more features, they have support and they are scalable and reliable. In the company I work for, we use Windows SharePoint Services to provide team sites to a group of people working on the same project. Sometimes there’s the need to have a very simple approval scenario, for which in my opinion the commercial solutions would be overkill and far too expensive (go talk to our management). Since we are a bunch of IT-people we can develop for each scenario a custom Event Handler implementation, which will work fine. But I just hate to create for each scenario a different Event Handler (I’m pretty quickly bored when doing repetitive work, you can ask Raf). That’s why I’ve come up with a more generic approach: a configurable Event Handler that will suit simple workflows. So will it be a better solution than the commercial ones: definitely not. Will it be as a reliable as the commercial products: probably not but I don’t intend to use it for critical processes. Will it be cheaper: I do think so since I’m developing it in my spare time (current rate €0.00 / hour, anyone want to pay more?) and configuration will be a matter of tweaking an XML file or something like that. Maybe the most important reason for me to start this project was the I’m intrigued by that kind of stuff. I want to know: can I make something like that, which problems will I have to conquer?

So, what’s the current status? Well because a video can probably say more than a 1000 words, I’ve recorded a small demo (actually I stole the recording idea from Patrick). Make sure you’ve installed Windows Media Player 9 Series and watch the video in full screen for your viewing pleasure.

So what’s going on in the video? It’s a very basic approval scenario, of course in a real life scenario the actions are executed by different people, but for simplicity the video shows only one actor. You can see how a document is submitted for review, it’s declined, submitted again and finally approved. If you watch closely you can see the Event Handler in action: sometimes there’s an additional page refresh needed to reflect the actions the Event Handler is doing in the background. Every action is triggered by setting properties (for more info see the previous post), based on the value of a property one or more actions are executed. As you can see at the end of the dem the Event Handler even can create list items that which can be used to keep an event log. I’ve decided to store the event log in an custom list (of course configurable which one, which fields etc.) and not in a field of the document, for several reasons:

  • The length of a text field of a Document Library item is limited to 255 characters (strange but true, although it seems that through the object model this limitation is not applicable).
  • You can’t set security for a specific field, so users could easily overwrite the event log. SharePoint Lists can be configured so users can only view the list for example.

At this point there are some issues for which I don’t have solution yet, maybe you can help me out:

  • I need to store the credentials (user name, password, domain) of the account that will be impersonated to do all the SharePoint actions. The easiest solution would be in the web.config file (appSettings section). But do you like having passwords sitting in plain text in a config file?
  • When you edit documents using Office 2003, you can change the value of the fields when you save the document. Of course the Event Handler will catch the insert or update event, but Office sets the status of that document to “Locked for editing” so the Event Handler can’t copy or update the document (exception: Document Locked The document you are attempting to edit is locked and cannot be updated.  It may be in use by another user or undergoing content indexing.)
    I haven’t found a solution to check in code if a document has the “Locked for editing” status. Although the real problem is that no events are raised when the status changes back to normal (when you close the document). Any thoughts?

Again your comments and feedback are greatly appreciated!

UPDATE: Visit the GotDotNet Workspace to download the engine!

Quite a lot of people are asking if Windows SharePoint Services offers workflows and/or approval routes. The answer is: no, not out-of-the-box. You can fairly easy extend SharePoint and attach an event handler to a document library. This event handler is nothing more than a .NET class that implements the IListEventSink interface and contains some logic corresponding to the desired actions. For more information around this topic you can read Patrick Tisseghem’s excellent article, or  this article from the MSDN Library. The downside of this approach is that for each specific workflow or approval route you need to code an event handler.Unless you create something more generic and configurable: let me introduce you to my new “pet project”!

I’ve created a generic event handler for SharePoint document libraries, that easily can be configured to create any workflow or approval route. Consider following approval route scenario to get an idea what you can accomplish with this event handler:
There is a SharePoint site with three document libraries: Work In Progress, In Review and Reviewed. Contributors can submit documents in the Work In Progress library, every contributor of the site has access to that library. Documents in this library have a custom choice field, named Status with following possible values: Work in progress, Ready to publish and Declined. Once a document in the library gets the Ready to publish status (assigned by the contributor), the document will move to the In Review document library. Only people that are Reviewers can access this library. The library also has a custom choice field, named Approval, which can have these values: Approve and Decline. Once a Reviewer has reviewed the document, he or she can set the Approval property to Approve or Declined. When the document is declined, it will be moved back to the Work In Progress document library. When to document is approved it will be moved to the Reviewed document library, which can be accessed by every member of that site.

I guess this scenario sounds pretty familiar, nothing fancy, just the basic needs of a lot of people. Now take a look at following XML configuration file:

It contains all the settings to use the scenario described above. You can see two WorkflowStage elements: there correspond with a document library which are a stage in the workflow process. In each stage you can assign triggers to properties. For example the trigger “Ready to publish” is assigned to the Status property, so when the Status field of a document in the document library changes to “Ready to publish”, the trigger will fire all the Actions it contains. Each action has a Type and up to three parameters. A property can have more than one trigger, for example the Approval property: based on the value of the Approval field the document is moved to the corresponding document library. At this point I’ve implemented following types:

  • MOVE: copies the document from the current library to a specific document library (value of Parameter1).
  • DELETESOURCE: removes the document from the current document library, for example after it’s copied to another library.
  • SETFIELDVALUE: sets a specific field to a value, for example to fill out a Reviewer field.
  • COPYFIELDVALUE: copies the value of a field from the originating document to the new document, for example after it’s copied.

But I’ve already several other possibilities in mind: send email, set field value to calculated value, … I think the event handler can cover quite a lot of basic workflow and approval route scenarios. If you have any comments to add or specific request, please let them know so I can implement them! When this project is ready to be published, I’ll release it so if you’re interested keep an eye on my blog. The goal of this project is to provide an easy and quick solution for basic workflow and approval route scenarios. It won’t replace commercial products that provide solutions for complex workflows. Let the feedback come!

UPDATE: Visit the GotDotNet Workspace to download the engine!

Several people reported some problems with the What’s New RSS Feed for SharePoint and the What’s New WebPart. When a user doesn’t has access to a SharePoint list, both components will try to obtain the necessary rights: the webpart will prompt for a new login and the RSS Feed will produce some “unexpected behaviour”. Before I jump into the technical details, here are the links to download the latest versions:

For each SharePoint list on a SharePoint site, the components need to check if the currently logged on user has rights to view the list. I did this using following line:
list.Permissions.DoesUserHavePermissions(SPRights.ViewListItems)
It seems that there are some problems using the DoesUserHavePermissions method, Philip Wheat goes into detail. Patrick Tisseghem recently posted his solution, and Philip responded. Anyway, I’ve used following code snippet to solve the issue:
private bool UserCanViewListItems(SPWeb web, SPList list)
{
            try
            {
                      list.Permissions.CheckPermissions(Microsoft.SharePoint.SPRights.ViewListItems);
                      return true;
            }
            catch
            {
                      return false;
            }
}
To use the function make sure you set the CatchAccessDeniedException property of the current site to false, eg:
web.Site.CatchAccessDeniedException = false;

Kudos to the SharePoint community: thanks guys!

Do you have BizTalk Server 2004 installed and you're Event Log is full of ENTSSO events?

Event Type: Error
Event Source: ENTSSO
Event Category: Enterprise Single Sign-On
Event ID: 10589
Date:  26-4-2004
Time:  13:03:37
User:  N/A
Computer: 
Description:
The master secret has not been backed up. If you lose the master secret all the information stored in the SSO system will be lost permanently and your systems may fail to work correctly. Please use the SSO admin tools to back up your master secret.

There's a very easy solution for this; simply use the SSOCONFIG.EXE utility that is installed in C:\Program Files\Common Files\Enterprise Single Sign-On. So open a command prompt, navigate to that directory and type this command:

ssoconfig -backupsecret mybackup.bak

The utility will ask for a password and a reminder, so it can backup the master secret into the file you've specified. What's better than an event log without red crosses? ;-)

Charles Young (subscribed!) posts a nice article about Orchestration transactions in BizTalk Server 2004:

BizTalk 2004 supports long-running and atomic transactions within Orchestrations.   The model has changed a bit from earlier versions, and atomic transactions are no longer based on DTC transactions by default.   This article explains some of the features of transactions, and also speculates about the exact behaviour of 'batched' atomic transactions - a subject that is currently very opaque due to a lack of documentation.

There are quite a lot of intresting BizTalk stuff on his blog, nice work Charles!

Just released on the MSDN Download Center:

  • Microsoft Office SharePoint Portal Server 2003 Administrator's Guide Version 2
    • This download contains information about managing Microsoft® Office SharePoint™ Portal Server 2003, including:
      • Planning
      • Deployment Scenarios
      • Installation
      • Security
      • Configuration
      • Administration
      • Backup and Restore
      • Maintenance
      • Customization
      • Troubleshooting
      • Reference
    • Revisions for this publication:
      • Updated content and fixed broken links.
      • Modified Microsoft Windows® SharePoint Services content to be applicable to SharePoint Portal Server 2003, including a major rewrite of Managing Site Groups and Permissions.
      • Added additional topics about Web Parts (About Web Parts, Managing a Site Collection Web Part Gallery, and Managing Web Parts on Virtual Servers).
      • Added Using SharePoint Configuration Analyzer.
  • Microsoft Office SharePoint Portal Server 2003 Help
    • This download contains information about using Microsoft® Office SharePoint™ Portal Server 2003, including:
      • Startup and Settings
      • Customizing the Portal Site
      • Personalizing the Portal Site
      • Working with Alerts
      • Searching the Portal Site
      • Working with Areas
      • Working with the Site Directory
      • Working with Backward-Compatible Document Libraries
      • Managing the Portal Site
      • Working with Windows® SharePoint Services
    • Revisions for this publication:
      • Updated content and fixed broken links.

InfoPath has a little bit of magic in it. I’m not talking about the cool features for creating and using rich XML based forms, but I mean the magic that happens when you save the contents of an InfoPath form in a XML file. Although the result is just a file with the XML extension, it seems even the Windows Explorer is enchanted, it displays an InfoPath icon for the XML file. When double click the file, InfoPath will be opened, the corresponding InfoPath form will be loaded and the data from the XML file is showed in the form.

So what’s causing of all this wizardry? If you open the XML file with Visual Studio.NET for example so we can see the actual XML content, you can see that are some XML directives (starting with ?mso) that you wouldn’t expect:

These tags are called Processing Instructions (PIs). Processing instructions are used to provide information in an XML document that the XML parser passes on to the calling application. PIs are primarily used to tell an application how to handle the data contained within an XML document. PIs must begin with an identifier called a target, which follows rules similar to those for element (XML element: An XML structure that consists of a start tag, an end tag, and the information between the tags. Elements can have attributes and can contain other elements.) and attribute (XML attribute: An XML structural construct. A name-value pair, separated by an equal sign and included in a tagged element, that modifies features of an element. All attribute values are text strings and must be enclosed in quotation marks.) names. Targets are case-sensitive and must start with a letter or underscore. The rest of the target can contain letters, numbers, hyphens, underscores, periods, and colons. Any valid XML textual characters can appear after the target. PIs can be placed anywhere in an XML document outside of other markup. They can be placed in the prolog, following the root element, or within the value of an element. They are usually placed in an XML document's prolog. (description from Office Online) Notice that the PIs also contain a reference to the published InfoPath form. In the example the form is located on my hard disk, but in real-world scenario’s it probably would have been in SharePoint or a file share.

So what got these PIs to do with BizTalk? Yesterday Scott Woodgate briefly mentioned the importance of the PIs in his webcast about BizTalk and InfoPath: when BizTalk picks up a message originating from InfoPath, the PIs could get lost when you process the message. Or if you have a BizTalk orchestration that would construct a new InfoPath message, you will probably want to add the PIs to that message so InfoPath will be used when the message is opened by a user. The first scenario is when you transform an InfoPath XML message using the BizTalk Mapper. By default the PIs will get lost, so the resulting message won’t have the PIs, but you can easily change this. There’s a property for the Grid of the map: “Copy Processing Instructions“; if you set this to Yes, PIs will be preserved in the new message.

A second way of adding PIs to a message in BizTalk is using expressions. You can write following code in an Expression or Assignment shape:
NewExpense = Expense;
NewExpense(XMLNORM.ProcessingInstructionOption) = 1;
NewExpense(XMLNORM.ProcessingInstruction) = "<?mso-infoPathSolution solutionVersion=\"1.0.0.1\" productVersion=\"11.0.6250\" PIVersion=\"1.0.0.0\" href=\"file:///C:\\InfoPath\\ExpenseReport.xsn\" name=\"urn:schemas-microsoft-com:office:infopath:ExpenseReport:-myXSD-2004-04-20T07-24-16\" ?><?mso-application progid=\"InfoPath.Document\"?>";
The value of the ProcessingInstructionOption property can be used as follows:

  • 0: New processing instructions from the XML Assembler are appended to the processing instructions at the beginning of the document.
  • 1: New processing instructions from the XML Assembler overwrite existing processing instructions at the beginning of the document.
  • 2: Processing instructions at the beginning of the document are removed.

Finally the third way of adding PIs is using a custom send pipeline. Add  a new Send Pipeline to your BizTalk project and in the Assemble Stage of the pipeline add a XML Assembler. Now you can set the “Add processing instructions text” property of that XML Assembler to the desired PIs. To every message send through this send pipeline, the PIs will be attached.

Conclusion? InfoPath and BizTalk work great together! Like Scott said: “InfoPath is to BizTalk as Outlook is to Exchange“. Check out his blog post about the webcast for more examples about InfoPath and BizTalk. If you've missed the webcast yesterday you can watch it on-demand when it becomes available (usually in 72 hours). Btw, there are a lot of intresting webcasts comming.

I wanted to create an application like this for ages, but Steve Clark beat me, nice work Steve! Description from his web log post (visit this link to download too):

First of all, I'll give a bit of background on why I decided to write this program. As I've been working on various parts of the SharePoint platform I've inevitably come across all kinds of helpful web site links. Often, I'll take a URL and forward it to people on the team or add it to our SharePoint Developer Center links list. When I was adding a favorite to a links list one day, I thought, "I should be able to do this directly from Internet Explorer, just like I subscribe to an RSS feed in Newsgator." Thus was born SharePointFav. Adding a URL to a SharePoint list has several obvious benefits:

  • Your links are now portable - no longer tied to your PC.
  • Your links are searchable using the powerful SharePoint engine. They are searchable for you and your colleagues.
  • You can add links to any SharePoint links list. If you have multiple sites covering multiple topics, this tool is for you.
  • You can share URLs with team members.
  • You can be alerted to new URLs on sites that you're interested in.

[Via ATG.Blog]

UPDATED HERE!!

Today I have a day off, so I decided to work a little bit on my “pet project”: RSS generation for SharePoint sites. Scoble asked for it when I released my RSS Reader Webpart. I know there are some good solutions for this problem available (for example from Siegfried Weber and DevHawk), but I was intrigued by this problem so I decided to craft one myself. Et voila: the What’s New Rss Feed for SharePoint was born (see the end of the post for downloads). I’ve re-used to logic of the What’s New Web Part to retrieve the latest changes on a SharePoint site so I won’t go into detail about that part of the code. The general idea behind this is that the code will go through all the lists on a SharePoint site (you can exclude specific lists) and retrieves the latest changed items (you can specify how many).

An APSX page will build the RSS Feed (more details the actual RSS generation later on) and will use the SharePoint object model to access the contents of a specific SharePoint site. The tricky part here is how the ASPX page can access the SharePoint object model. This issue is solved by deploying the ASPX page to the _VTI_BIN directory of a SharePoint site. You may have noticed this, but this directory is kind-a special: this directory is accessible from each SharePoint site, e.g.: http://sharepoint.leadit.be/_vti_bin/... , http://sharepoint.leadit.be/SubSite/_vti_bin/... . It doesn’t matter which URL you use, you’ll always end up in the same virtual _VTI_BIN directory. To find out where this magical virtual directory maps to on your hard disk, you can use the IIS Manager: navigate to the _VTI_BIN directory and check the properties. The default for the Local Path property is “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\isapi”. So we need to deploy the ASPX page to this directory and of course the corresponding assembly to \BIN in this directory.

The next problem to solve is how to get a hold of the SharePoint web object we want to access. Actually this is quite simple: the GetContextWeb function of the SPControl class can be used to get a SPWeb instance. Based on the Context we’re in, we’ll get the corresponding SPWeb instance.
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
SPWeb web = SPControl.GetContextWeb(Context);

To construct the contents of the RSS Feed, I’ve used the open source RSS.NET library. This library allows you to build an RSS Feed through a very nice object model. When you’re done you can write the contents to the Response stream:
RssFeed rssFeed = new RssFeed();
rssFeed.Channels.Add(rssChannel);
Response.ContentType = "text/xml";
Response.ExpiresAbsolute = DateTime.MinValue;
rssFeed.Write(Response.OutputStream);

To deploy the APSX page, you only have to copy WhatsNewRss.aspx to the directory that maps to the _VTI_BIN virtual directory (probably C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\isapi) and Leadit.SharePoint.Services.dll to the corresponding BIN directory (probably C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\isapi\BIN). Now you should be able to browse to the ASPX page through a SharePoint URL, for example http://sharepoint.leadit.be/_vti_bin/WhatsNewRss.aspx. There are three parameters (which you can add to the URL) that you can use to alter the contents of the feed (similar to the properties of the What’s New Web Part):

  • ExcludeLists
    Specify which lists you want to exclude, separated by a semi-colon (;).
    Default value: Web Part Gallery;fpdatasources
  • ListFields
    Specify what field for a specific list should be used as a title.
    Default value: Contacts=Full Name;Links=URL
  • ItemsToDisplay
    Specify how many items to display.
    Default value: 15

Example:
http://localhost:8000/_vti_bin/WhatsNewRss.aspx?ItemsToDisplay=5&ExcludeLists=MyList&ListFields=Contacts=Last Name

The result is an RSS Feed which you can view in any feed reader, for example SharpReader:

As always, please test before you put this thing in production (remember this is V1). Let me know if you have any problems! One final note: for some feed readers it’s necessary to alter the Directory Security for the _VTI_BIN virtual directory. To access the feed from these feed readers you need to allow Basic Authentication:

Downloads:

Here's a nice tip I found in the BizTalk newsgroup, which is a great resource for learning more about BizTalk. When you process positional flat files, BizTalk assumes that each line is completely filled with characters. For example you have two fields in your flat file: code (5 positions) and name (10 positions): BizTalk expects the file to look like this:

codeANameA12345<- end of line
codeBNameB     <- end of line

So even the name field of the second line only has 5 characters, the remaining 5 need to be filled with spaces. Suppose your file has the end of line directly after the contents of the name field ends:

codeANameA12345<- end of line
codeBNameB<- end of line

When you try to process this file, BizTalk will throw an exception: Unexpected data found while looking for:'\r\n'. David Downing (MSFT) has the solution: you can add the following annotation to the schemaInfo node:

allow_early_termination="true"

You'll have to edit the schema using a text editor because the BizTalk schema editor doesn't allow you to add this annotation. Now BizTalk will allow the last field to be early terminated. Christof Claessens suggests in a post that there are more of these undocumented hints, I'd love to see them published! :-)

More Posts Next page »