Creating workflows using Sharepoint Designer
It should be noted that any workflow you create using Sharepoint Designer is effectively a sequential workflow, wherein you define the sequence of the steps to be performed. Each step being composed of actions and conditions. Any new functionality would require you to create new custom activity in VS.NET and deploy it as an action such that it could be used from within Sharepoint Designer.
Some of the stuff noted in case of sharepoint designer (SD) :
a.) It does not allow for coding/scripting. The best you can define are rules.
b.) The association with a list/library/content type is done immediately when the workflow is first created.
c.) Unlike workflows created in VS.NET, SD does not allow for modification of active workflow.
Breaking out from SD
In case the limitations of sharepoint designer gets on your head, you might want to export the workflow from sharepoint designer to VS.NET. For this :
a.) Export the workflow as an FWP from within the designer.
b.) Rename .FWP to .CAB.
c.) Extract the .CAB file.
d.) Open the extracted .XOML, rules and other files from within VS.NET.
VS.NET namespaces
In addition to the activities which WWF provides, MOSS adds onto this list by enhancing the Base Activity Library (BAL) using custom activities. These activities are available in Microsoft.Sharepoint.WorkflowActions & Microsoft.Sharepoint.Workflow.
Interaction Points
From a developer perspective, there are usually four points in the life of a workflow task when the end user can interact with the workflow. Effectively, its these four interaction points that can be handled/customised by the developer.
Association : When the workflow template is associated the first time with a library/document/content type.
Initialization : When the workflow instance is initialized/created.
Completion : When each user completes their task/step.
Modification : When the workflow itself is modified.
All of the above interactions require you to create custom ASPX pages (for both WSS and MOSS). In case of MOSS, you also have the option to create these forms in InfoPath, which tends to be a lot easier to develop. The table at this blog should clear up any queries regarding which event to handle at each of the interaction points.
Developer Notes
1.) Unlike a WWF workflow, the base class the sharepoint workflow is either SharePointSequentialWorkflowActivity or SharePointStateWorkflowActivity based on your workflow type (sequential or state).
2.) OnWorkFloActivated needs to be first activity for a sharepoint workflow.
3.) Correlation Token (we had discussed this in an earlier post) needs to be set the same for the related activities.
4.) In WWF while you must have used ExternalDataExchangeAttribute , in MOSS most of the data exchange stuff is handled internally. (Is this a right assumption ?)
5.) DependencyProperty makes it simpler to access workflow object properties which are available after activation.
Showing posts with label Sharepoint. Show all posts
Showing posts with label Sharepoint. Show all posts
Monday, 3 March 2008
Wednesday, 27 February 2008
Revisting Workflows in Sharepoint - Part 1
There are quite a few stuff which remains to be talked about workflows in Sharepoint, the ones we did not cover in the earlier posts (filter the blog posts by the WWF tag). Lets revisit some of these in this post.
We now know that workflows act as a facilitator for automating system (including human) interactions and let you define the business process. WWF being the framework provided by Microsoft for building these workflows using the concept of Activities. Note that WWF itself could be hosted in many possible environments, ranging from a .NET console application to a service.
In case of MOSS, the WWF is hosted within the MOSS environment. MOSS also appears to have replaced the following core WWF services within WWF to its needs - transaction, persistence, notification, roles, messaging. While using WWF workflows in MOSS, the idea is to link the workflow template which we created using WWF with either a list/document type or content type as the first step. Once this is done, instances of these items (documents/list/content type) could trigger off a workflow instance (based off a workflow template).
You could configure MOSS to create a new workflow instance when either of the following happens:
a.) Automatically, say whenever an item is added to the library
b.) Manually start the workflow against an item instance
c.) Or start the workflow by writing code.
In MOSS terms, the flow of work is notified to the user by using 'tasks'. User tasks gets shown up in their tasks list and also against the item's task list. You would usually configure to sent email notifications to the user simultaneously when the task is created. Completing one task might create further tasks for different users in normal scenarios.
OOTB Workflows and Office Support
Out of the box, MOSS provides workflows (Signature collection, feedback collection, translation management etc) which could be used with minimal configuration. Do note that WSS does not provide these OOTB workflows, though it should not be difficult to create one.
The name change from sharepoint server to MOSS and the related integration with Office 2007 based systems are quite extensive. You can now create MOSS workflows against new documents in MS Word, synchronise MOSS tasks against outlook tasks such that most/all of the workflow related actions can be accomplished from within the office 2007 systems, without ever visiting MOSS portals. Do note that to exploit Office 2007 systems, you would need to make sure that the interacting pages in MOSS are created in InfoPath.
In the next post while we revist more into workflows in MOSS, lets have a developer perspective.
We now know that workflows act as a facilitator for automating system (including human) interactions and let you define the business process. WWF being the framework provided by Microsoft for building these workflows using the concept of Activities. Note that WWF itself could be hosted in many possible environments, ranging from a .NET console application to a service.
In case of MOSS, the WWF is hosted within the MOSS environment. MOSS also appears to have replaced the following core WWF services within WWF to its needs - transaction, persistence, notification, roles, messaging. While using WWF workflows in MOSS, the idea is to link the workflow template which we created using WWF with either a list/document type or content type as the first step. Once this is done, instances of these items (documents/list/content type) could trigger off a workflow instance (based off a workflow template).
You could configure MOSS to create a new workflow instance when either of the following happens:
a.) Automatically, say whenever an item is added to the library
b.) Manually start the workflow against an item instance
c.) Or start the workflow by writing code.
In MOSS terms, the flow of work is notified to the user by using 'tasks'. User tasks gets shown up in their tasks list and also against the item's task list. You would usually configure to sent email notifications to the user simultaneously when the task is created. Completing one task might create further tasks for different users in normal scenarios.
OOTB Workflows and Office Support
Out of the box, MOSS provides workflows (Signature collection, feedback collection, translation management etc) which could be used with minimal configuration. Do note that WSS does not provide these OOTB workflows, though it should not be difficult to create one.
The name change from sharepoint server to MOSS and the related integration with Office 2007 based systems are quite extensive. You can now create MOSS workflows against new documents in MS Word, synchronise MOSS tasks against outlook tasks such that most/all of the workflow related actions can be accomplished from within the office 2007 systems, without ever visiting MOSS portals. Do note that to exploit Office 2007 systems, you would need to make sure that the interacting pages in MOSS are created in InfoPath.
In the next post while we revist more into workflows in MOSS, lets have a developer perspective.
Tuesday, 26 February 2008
Webparts in MOSS 2007 - Part 4
The examples we have seen until now talks about webparts that worked independently (and good :)). Assuming we need to get two webparts to talk with each other, you need to follow this :
1.) Setup a data object interface which would be shared by the two webparts.
2.) On the source/provider webpart, perform the following actions :
a.) Implement our data object interface. The below sample returns the value from a text field.
b.) Setup a connection point which the consumer object can use. For this, use the ConnectionProvider attribute.
3.) On the consumer webpart , access the connection point (and effectively the shared data) by implementing a method which is marked with the ConnectionConsumer attribute.
Note that the argument of this custom method is set to the shared interface. When MOSS calls this routine (since we have used the ConnectionConsumer attribute), the parameter provider would be set to the source webpart's interface. Save this value in a local variable for later usage.
Do note that the value maintained in the above private variable was available for usage ONLY the PreRender function of the WebPart. Is it because I have not saved it in a session object ?
Anyways, the above bits should get you started with inter webpart communication. Once you have deployed the above two webparts, use the design mode of the zone to setup the source and destination connections.
Note that for intra-webzone webpart communication, things appear to get a bit different. I havent tried it, but you would need to start with changing the parent class for the WebPart (mentioned in Part 1) .
1.) Setup a data object interface which would be shared by the two webparts.
public interface ISharedData
{
string data { get; set; }
}
2.) On the source/provider webpart, perform the following actions :
a.) Implement our data object interface. The below sample returns the value from a text field.
string ISharedData.data
{
get { return txtData.Text; }
set { txtData.Text = value; }
}
b.) Setup a connection point which the consumer object can use. For this, use the ConnectionProvider attribute.
[ConnectionProvider("Sample Shared Data Provider")]
public ISharedData GetCommunicationPoint()
{
return this as ISharedData;
}
3.) On the consumer webpart , access the connection point (and effectively the shared data) by implementing a method which is marked with the ConnectionConsumer attribute.
[ConnectionConsumer("Shared Data Consumer")]
public void InitializeProvider(ISharedData provider)
{
SharedData = provider;
}
Note that the argument of this custom method is set to the shared interface. When MOSS calls this routine (since we have used the ConnectionConsumer attribute), the parameter provider would be set to the source webpart's interface. Save this value in a local variable for later usage.
Do note that the value maintained in the above private variable was available for usage ONLY the PreRender function of the WebPart. Is it because I have not saved it in a session object ?
protected override void OnPreRender(EventArgs e)
{
if (_sharedData != null)
{
/*use the shared data*/
lblData.Text = "Data from other web part = " + _sharedData.data;
}
}
Anyways, the above bits should get you started with inter webpart communication. Once you have deployed the above two webparts, use the design mode of the zone to setup the source and destination connections.
Note that for intra-webzone webpart communication, things appear to get a bit different. I havent tried it, but you would need to start with changing the parent class for the WebPart (mentioned in Part 1) .
Webparts in MOSS 2007 - Part 3
We might have user customisable properties within our webpart which we think could be customised during design time. Here design time means the mode when you add the webpart into the appropriate webzone from the webpart gallery. At this point, all properties which had the WebBrowsable attribute set (check out Part 1) are available for customization.
For simple properties (say integers/string etc), MOSS automatically renders the editors for you. It even figures out the enumerations and renders the appropriate listbox..all good. For properties which need customisation, say you need to list down custom values from a DB, you would need to create a custom editor class separately and map it within the webpart.
Steps
1.) Create the editor object for the webpart
a.) Create a new class deriving from EditorPart class and create any control which you would need to use by overriding the CreateChildControls(), similar to the way we created child controls for WebParts.
b.) Now listen carefully. When we think about the Editor which would be shown, there are two important data handling instances - when the editor is shown the first time, we would need to load the currently set value in the webpart and when the editor value is applied/saved, we need to save the selected value back to the webpart.
To handle these two instances, we need to override the SyncChanges() and ApplyChanges() routines. The WebPart being edited at these points is available using the WebPartToEdit property.
Thats it, the webpart editor is ready. What is remaining is linking this editor with the WebPart.
2.) Link the editor with the WebPart.
Our WebPart needs to implement the IWebEditable interface to tell MOSS that it supports custom editors such that the right editor gets shown when our webpart is selected.
IWebEditable requires one method and one get property to be implemented:
Method CreateEditorParts - Override this method to specify all the editors which you need this web part to use (havent tried with more than one). It is at this routine we add our custom editor which we just created and return the editors list.
Property WebBrowsableObject - This expects you to return the current webpart which is currently being edited. (simplest to implement I guess :) )
Once we have the above two steps done, we are nearly done with the editor. Following is a code snippet for a quick look:
Next time lets look at inter communication between webparts :)
For simple properties (say integers/string etc), MOSS automatically renders the editors for you. It even figures out the enumerations and renders the appropriate listbox..all good. For properties which need customisation, say you need to list down custom values from a DB, you would need to create a custom editor class separately and map it within the webpart.
Steps
1.) Create the editor object for the webpart
a.) Create a new class deriving from EditorPart class and create any control which you would need to use by overriding the CreateChildControls(), similar to the way we created child controls for WebParts.
b.) Now listen carefully. When we think about the Editor which would be shown, there are two important data handling instances - when the editor is shown the first time, we would need to load the currently set value in the webpart and when the editor value is applied/saved, we need to save the selected value back to the webpart.
To handle these two instances, we need to override the SyncChanges() and ApplyChanges() routines. The WebPart being edited at these points is available using the WebPartToEdit property.
Thats it, the webpart editor is ready. What is remaining is linking this editor with the WebPart.
2.) Link the editor with the WebPart.
Our WebPart needs to implement the IWebEditable interface to tell MOSS that it supports custom editors such that the right editor gets shown when our webpart is selected.
IWebEditable requires one method and one get property to be implemented:
Method CreateEditorParts - Override this method to specify all the editors which you need this web part to use (havent tried with more than one). It is at this routine we add our custom editor which we just created and return the editors list.
Property WebBrowsableObject - This expects you to return the current webpart which is currently being edited. (simplest to implement I guess :) )
Once we have the above two steps done, we are nearly done with the editor. Following is a code snippet for a quick look:
public class CustomEditorWebPart : WebPart, IWebEditable
{
private string _reviewer;
public string Reviewer
{
get { return _reviewer; }
set { _reviewer = value; }
}
#region IWebEditable Members
EditorPartCollection IWebEditable.CreateEditorParts()
{
List<EditorPart> editors = new List<EditorPart>();
editors.Add(new ReviewerEditor());
return new EditorPartCollection(editors);
}
object IWebEditable.WebBrowsableObject
{
get { return this; }
}
#endregion
protected override void Render(HtmlTextWriter writer)
{
writer.Write("Reviewer : " + Reviewer);
}
}
public class ReviewerEditor : EditorPart
{
private ListBox myListBox;
public ReviewerEditor()
{
ID = "ReviewerEditor";
Title = "Edit the Reviewer";
}
protected override void CreateChildControls()
{
myListBox = new ListBox();
myListBox.Items.Add("John");
myListBox.Items.Add("Thomas");
myListBox.Items.Add("Bipin");
Controls.Add(myListBox);
}
public override bool ApplyChanges()
{
EnsureChildControls();
CustomEditorWebPart oPart = (CustomEditorWebPart) WebPartToEdit;
oPart.Reviewer = myListBox.SelectedValue;
return true;
}
public override void SyncChanges()
{
EnsureChildControls();
CustomEditorWebPart oPart = (CustomEditorWebPart) WebPartToEdit;
myListBox.SelectedValue = oPart.Reviewer;
}
}
Next time lets look at inter communication between webparts :)
Webparts in MOSS 2007 - Part 2
In the first part, we talked about creating the simplest of WebPart and the classes of concern. This time, lets render something meaningfull in our webpart. How about the list of all the users in the system ? We would be using the MOSS libraries to get this information.
Steps
As usual, create a blank class derived from System.Web.UI.WebControls.WebParts.WebPart and override the render method as :
As seen, the GetHTML function returns the HTML string which needs to be rendered at the Render() function. GetHTML uses the server context together with the UserProfileManager to get all the user profiles in the Sharepoint system. In addition to this, we have rendered a link for each of the user such that it takes you to the home page of the user.
You would need to include the following namespaces in the 'using' section if not already done : Microsoft.Office.Server, Microsoft.Office.Server.UserProfiles, Microsoft.SharePoint.Utilities, Microsoft.SharePoint.
Follow the either of the two steps mentioned in the previous post to register this webpart on the server and test it out.
More rendering with data from the DB
Lets create another WebPart which renders data from the DB onto a DataGrid. The core idea remains the same. You perform the render on the items that you know and for your child controls (DataGrid, Label etc), you ask them to render themselves.The crux of the code is contained in the following :
Note the usage of RenderBeginTag and RenderEndTag which generates the matching start and end tags. Within each of the tags, we render the specific control. As seen, the DataGrid (gridProductList) gets rendered in the appropriate table column within a html row.
LoadData() function referred in Render() basically loads the data into the DataGrid using standard data access calls. Any child control the webpart uses should desirably be created at the CreateChildControls method. Though we might as well do this when the WebPart gets created, writing it here ensures that EnsureChildControls() call it when required.
I hope this gives you a gist of how WebParts are created and used. In the next parts, we shall see about Custom Editors and inter WebPart communication.
Steps
As usual, create a blank class derived from System.Web.UI.WebControls.WebParts.WebPart and override the render method as :
protected override void Render(HtmlTextWriter writer)
{
writer.Write(GetHTML());
}
private string GetHTML()
{
string result = "<table border=\"0\">";
try
{
ServerContext context = ServerContext.GetContext(Context);
UserProfileManager profileManager = new UserProfileManager(context);
foreach (UserProfile profile in profileManager)
{
if (profile.PublicUrl.AbsoluteUri != null)
{
result += "<tr><td><a href=\"" + SPEncode.HtmlEncode(profile.PublicUrl.AbsoluteUri) + "\"/>" +
SPEncode.HtmlEncode(profile[PropertyConstants.AccountName].ToString()) + "</a></td></tr>";
}
}
result += "</table>";
}
catch (Exception ex)
{
result += "<tr><td>" + ex.ToString() + "</td></tr></table>";
}
return result;
}
As seen, the GetHTML function returns the HTML string which needs to be rendered at the Render() function. GetHTML uses the server context together with the UserProfileManager to get all the user profiles in the Sharepoint system. In addition to this, we have rendered a link for each of the user such that it takes you to the home page of the user.
You would need to include the following namespaces in the 'using' section if not already done : Microsoft.Office.Server, Microsoft.Office.Server.UserProfiles, Microsoft.SharePoint.Utilities, Microsoft.SharePoint.
Follow the either of the two steps mentioned in the previous post to register this webpart on the server and test it out.
More rendering with data from the DB
Lets create another WebPart which renders data from the DB onto a DataGrid. The core idea remains the same. You perform the render on the items that you know and for your child controls (DataGrid, Label etc), you ask them to render themselves.The crux of the code is contained in the following :
protected override void Render(HtmlTextWriter writer)
{
EnsureChildControls(); //makes sure the child control were created
LoadData();
writer.RenderBeginTag("table");
writer.RenderBeginTag("tr");
writer.RenderBeginTag("td");
lblSubmit.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderBeginTag("tr");
writer.RenderBeginTag("td");
gridProductList.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderEndTag(); //end table
}
protected override void CreateChildControls()
{
base.CreateChildControls();
lblSubmit = new Label();
lblSubmit.Text = "Employee List";
Controls.Add(lblSubmit);
gridProductList = new DataGrid();
Controls.Add(gridProductList);
}
Note the usage of RenderBeginTag and RenderEndTag which generates the matching start and end tags. Within each of the tags, we render the specific control. As seen, the DataGrid (gridProductList) gets rendered in the appropriate table column within a html row.
LoadData() function referred in Render() basically loads the data into the DataGrid using standard data access calls. Any child control the webpart uses should desirably be created at the CreateChildControls method. Though we might as well do this when the WebPart gets created, writing it here ensures that EnsureChildControls() call it when required.
I hope this gives you a gist of how WebParts are created and used. In the next parts, we shall see about Custom Editors and inter WebPart communication.
Monday, 25 February 2008
Webparts in MOSS 2007 - Part 1
Webparts can be considered as reusable widgets (similar to the yahoo ones) which work independently (though they can communicate) and are individually configurable. This makes the task of showing up of different logical sections on the same web page easier; especially while doing independent development. Brought out initially with Sharepoint 2003, it was supported in ASP.NET extensively and now in MOSS 2007 .
OOB WebParts
Out of the box, MOSS provides you with numerous webparts which could be used with minimal configuration. You would want to check out the webpart gallery which list down these.
Some of the interesting OOB webparts :
Image Webpart : To display images from sites/from another webpart
Site Aggregator : To display sites of your choice
RSS Viewer : To get feeds from any RSSContent Query : To display a content type.
Business Data List : List from LOB/Webservice configured in BDC
Development Concerns
As soon as you start to swim through webpart documentations, you are faced with the dilemma of two parent WebPart classes which provide more or less the same functionality. Which one do you use? From what I could figure out, System.Web.UI.WebControls.WebParts.WebPart is the most commonly used for WebPart development and the simplest.
The immediate descendant class defined in Microsoft.SharePoint.WebPartPages.WebPart was intentionally provided for compatibility with Sharepoint 2003 and also for performing complex webpart functionalities which include cross page web part communication, communication between webparts not in the same webzone, data caching etc.
The good part is, if you use the System.Web.UI.... WebPart, you could reuse the webpart in an ASP.NET application too, assuming your webpart does not have any MOSS specific calls. To summarise, stick onto the System.Web...WebPart for most of your regular requirements .
Classes of concern
WebPartManager - Microsoft (and now me) loves manager and provider classes. In this case, WebPartManager acts as the point of entry to access the various features of the various webparts in a page. This means that there is exactly one WebPartManager for a webpage. If you are using the masterpage provided by MOSS 2007, this would mean that the WebPartManager is already available to you (things get different when you do plain ASP.NET development)
WebPartZone - This is physical place/zone on the page where WebParts reside.
Writing the first WebPart
Now that we are decided on the parent webpart class to use for our WebPart, the main job we have is to tell what needs to be rendered. Simple enough, override the render function :)
OK, so who is going to talk about the attributes? Here goes:
WebBrowsable - makes sure that this property is listed in the property editor when the WebPart is configured.
Personalizable - Setting a value of true on this attribute makes sure that the property value is maintained for individual users.
FriendlyName - The easiest of the lot, this displays the friendly text for this property in the property editor.
Deploying a WebPart
There are two ways to deploy a webpart to the server :
a.) Direct copy/register Copy the WebPart dll to the _app_bin of your application and mark the assembly as safe in web.config. There you go, WebPart is all setup in your current application and ready to use.
b.) CAB This is the recommended way to deploy webparts in the live environment. What you do is create a DWP (dashboard web part) XML file together with a manifest file, wrap it up in a CAB project. Use this CAB project together with the stsadm command line tool at the server to register your WebPart. Check out more about DWP files here.
In the next post, we shall look at rendering some meaningful stuff at the render method, Property Editors, Inter Communication between WebParts.
OOB WebParts
Out of the box, MOSS provides you with numerous webparts which could be used with minimal configuration. You would want to check out the webpart gallery which list down these.
Some of the interesting OOB webparts :
Image Webpart : To display images from sites/from another webpart
Site Aggregator : To display sites of your choice
RSS Viewer : To get feeds from any RSSContent Query : To display a content type.
Business Data List : List from LOB/Webservice configured in BDC
Development Concerns
As soon as you start to swim through webpart documentations, you are faced with the dilemma of two parent WebPart classes which provide more or less the same functionality. Which one do you use? From what I could figure out, System.Web.UI.WebControls.WebParts.WebPart is the most commonly used for WebPart development and the simplest.
The immediate descendant class defined in Microsoft.SharePoint.WebPartPages.WebPart was intentionally provided for compatibility with Sharepoint 2003 and also for performing complex webpart functionalities which include cross page web part communication, communication between webparts not in the same webzone, data caching etc.
The good part is, if you use the System.Web.UI.... WebPart, you could reuse the webpart in an ASP.NET application too, assuming your webpart does not have any MOSS specific calls. To summarise, stick onto the System.Web...WebPart for most of your regular requirements .
Classes of concern
WebPartManager - Microsoft (and now me) loves manager and provider classes. In this case, WebPartManager acts as the point of entry to access the various features of the various webparts in a page. This means that there is exactly one WebPartManager for a webpage. If you are using the masterpage provided by MOSS 2007, this would mean that the WebPartManager is already available to you (things get different when you do plain ASP.NET development)
WebPartZone - This is physical place/zone on the page where WebParts reside.
Writing the first WebPart
Now that we are decided on the parent webpart class to use for our WebPart, the main job we have is to tell what needs to be rendered. Simple enough, override the render function :)
public class MyFirstWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
private string displayText = "MOSS Rocks";
[WebBrowsable(true), Personalizable(true), FriendlyName("Display Text")]
public string DisplayText
{
get { return displayText; }
set { displayText = value; }
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
writer.Write("Typed Text is " + displayText);
}
}
OK, so who is going to talk about the attributes? Here goes:
WebBrowsable - makes sure that this property is listed in the property editor when the WebPart is configured.
Personalizable - Setting a value of true on this attribute makes sure that the property value is maintained for individual users.
FriendlyName - The easiest of the lot, this displays the friendly text for this property in the property editor.
Deploying a WebPart
There are two ways to deploy a webpart to the server :
a.) Direct copy/register Copy the WebPart dll to the _app_bin of your application and mark the assembly as safe in web.config. There you go, WebPart is all setup in your current application and ready to use.
b.) CAB This is the recommended way to deploy webparts in the live environment. What you do is create a DWP (dashboard web part) XML file together with a manifest file, wrap it up in a CAB project. Use this CAB project together with the stsadm command line tool at the server to register your WebPart. Check out more about DWP files here.
In the next post, we shall look at rendering some meaningful stuff at the render method, Property Editors, Inter Communication between WebParts.
Monday, 15 October 2007
Audience in MOSS
Concept
Audience feature in MOSS should not be mixed up with security features/trimming of data based on the user credentials and the resulting authorization. Instead, 'audience' lets you filter out undesirable data for the current user context. E.g.:- you would want to hide the sales data on the home page if the current user is not part of the sales team; this makes sure the marketing (or other) teams are not overloaded with unwanted information.
In MOSS, the idea is to setup a user/audience list such that webparts/lists can later show/hide the required information. This is supported out of the box.
Setting Up an Audience
From a shared service provider, you could add new audiences by creating rules using windows groups or distribution list or any of the property available against a user (name, address, department, manager etc).
To extend this audience definition, we could have custom property (say 'Day/Night Shift') added against a user from the Shared Services->User Profile settings. This makes sure that we can create rules based on this new custom property to show items specific to employee working in the day shift.
Consuming an Audience
Out of the box, sharepoint lets you apply audience-targeting for the following items
a.) WebParts - while designing the webpart, you could specify the target audience for this webpart. In this case, the webpart would be rendered only if the current-user is part of the target audience. In all other cases, the webpart would not be visible / rendered.
b.) Lists - While designing a list, you could specify that the list needs to have audience targeting available. In this case, you could use Content Query WebPart to filter out data
Consuming Audience programmatically
AudienceManager class - Acts as the entry point into the entire functionality of audience in MOSS. This class also implements the IRuntimeFilter interface to perform the targeting functionalities for webpart/lists.
For a custom user control to consume the Audience functionalities, one of the way is to setup an audience list for the custom control (a new property perhaps) such that at runtime, the control can use AudienceManager.IsCurrentUserInAudienceOf() method to check if the active user is part of the audience previously setup. Note that in this case, it’s up to the control developer to implement the required functionality of hiding/filtering data etc.
Audience feature in MOSS should not be mixed up with security features/trimming of data based on the user credentials and the resulting authorization. Instead, 'audience' lets you filter out undesirable data for the current user context. E.g.:- you would want to hide the sales data on the home page if the current user is not part of the sales team; this makes sure the marketing (or other) teams are not overloaded with unwanted information.
In MOSS, the idea is to setup a user/audience list such that webparts/lists can later show/hide the required information. This is supported out of the box.
Setting Up an Audience
From a shared service provider, you could add new audiences by creating rules using windows groups or distribution list or any of the property available against a user (name, address, department, manager etc).
To extend this audience definition, we could have custom property (say 'Day/Night Shift') added against a user from the Shared Services->User Profile settings. This makes sure that we can create rules based on this new custom property to show items specific to employee working in the day shift.
Consuming an Audience
Out of the box, sharepoint lets you apply audience-targeting for the following items
a.) WebParts - while designing the webpart, you could specify the target audience for this webpart. In this case, the webpart would be rendered only if the current-user is part of the target audience. In all other cases, the webpart would not be visible / rendered.
b.) Lists - While designing a list, you could specify that the list needs to have audience targeting available. In this case, you could use Content Query WebPart to filter out data
Consuming Audience programmatically
AudienceManager class - Acts as the entry point into the entire functionality of audience in MOSS. This class also implements the IRuntimeFilter interface to perform the targeting functionalities for webpart/lists.
For a custom user control to consume the Audience functionalities, one of the way is to setup an audience list for the custom control (a new property perhaps) such that at runtime, the control can use AudienceManager.IsCurrentUserInAudienceOf() method to check if the active user is part of the audience previously setup. Note that in this case, it’s up to the control developer to implement the required functionality of hiding/filtering data etc.
Thursday, 19 April 2007
WWF - Persisting WorkFlow
Why Persist?
The workflow host, which could be anything from a console application to a full fledged service application like MOSS, is not expected to maintain the state of the workflow instance in memory all the time. This is simply to save server resources and make them available. This considers the fact that workflows could be running for days.
Persisting - The common path
To save/persist/dehydrate/stream/serialize (yes, all denote the same idea conceptually) workflows, you usually use a workflow persistence service object such as the SqlWorkflowPersistenceService. This object could either be consumed directly within your own hosting application codebase or set up via a config file.In either of the case, you could ask for an automatic save when the workflow is 'idle' using the UnloadOnIdle entry.
What needs to be noted here is that all objects used by our workflow should be serializable in order for the host to persist the workflow (and the related objects) OK. An exception is guaranteed otherwise.
When does the save happen?
The workflow runtime appears to persist the workflow on these scenarios ('persist points') :
Against an activity, when it gets completed. (Check out the PersistOnCloseAttribute declared against Activities.)
When the workflow is completed or idle (delays, event waits)
When the workflow is forcefully unloaded.
Writing Custom Persistence Layer
Overriding a few functions by descending from the WorkflowPersistenceService class makes it easy to write a custom persistence class. Further, this new class could be made active against the workflow via the config file. But, most of us should be happy with the out of box SqlWorkflowPersistenceService which does seem to do the job good.
Persistence under MOSS
MOSS as a host has its own persisting service which uses the SPWinOePersistenceService object by default. [haven't tried forcing a different persistence object via the config though]. Waiting for external actions which include delays, waiting for events to fire etc causes the workflow to be persisted/saved to DB. The workflow appears to be serialized to the WorkFlow table (check out the InstanceData column) in the Content DB for the site.
The workflow host, which could be anything from a console application to a full fledged service application like MOSS, is not expected to maintain the state of the workflow instance in memory all the time. This is simply to save server resources and make them available. This considers the fact that workflows could be running for days.
Persisting - The common path
To save/persist/dehydrate/stream/serialize (yes, all denote the same idea conceptually) workflows, you usually use a workflow persistence service object such as the SqlWorkflowPersistenceService. This object could either be consumed directly within your own hosting application codebase or set up via a config file.In either of the case, you could ask for an automatic save when the workflow is 'idle' using the UnloadOnIdle entry.
What needs to be noted here is that all objects used by our workflow should be serializable in order for the host to persist the workflow (and the related objects) OK. An exception is guaranteed otherwise.
When does the save happen?
The workflow runtime appears to persist the workflow on these scenarios ('persist points') :
Against an activity, when it gets completed. (Check out the PersistOnCloseAttribute declared against Activities.)
When the workflow is completed or idle (delays, event waits)
When the workflow is forcefully unloaded.
Writing Custom Persistence Layer
Overriding a few functions by descending from the WorkflowPersistenceService class makes it easy to write a custom persistence class. Further, this new class could be made active against the workflow via the config file. But, most of us should be happy with the out of box SqlWorkflowPersistenceService which does seem to do the job good.
Persistence under MOSS
MOSS as a host has its own persisting service which uses the SPWinOePersistenceService object by default. [haven't tried forcing a different persistence object via the config though]. Waiting for external actions which include delays, waiting for events to fire etc causes the workflow to be persisted/saved to DB. The workflow appears to be serialized to the WorkFlow table (check out the InstanceData column) in the Content DB for the site.
Wednesday, 18 April 2007
Sharepoint - Integrating MOSS+WWF+ASPX - Part 3
Exposing our WWF workflow to MOSS
There are two more bits we need to do in the WWF workflow application to make it available to MOSS. We need to declare the feature and the workflow xml.
feature.xml
MOSS introduced features for the developers to create site items/functionality which can later be linked with sharepoint collectons/sites. Within this XML file, you would also tell which XML would contain the feature specific details - in our case workflow.xml.
workflow.xml
Describes stuff about our workflow to sharepoint, including name, description , id etc. This also defines the pages which would be used for workflow Instantiation, association and modification. We shall have a look at an example of Instantiation later in a different blog entry. Modification of workflow (say you would to add more reviewers) at runtime needs a few extra steps and this is when the modification page comes into effect.
Defining Custom Pages for Task Initiation
For ease, all of these three pages needs to derive from Microsoft.Sharepoint.WebControls.LayoutsPageBase with sharepoint master pages (~/_layouts/application.master) being used in the ASPX definition. MOSS provides a lot many master pages which give the consistent look and feel of standard MOSS pages. The content placeholders within the masterpages would need to be filled in by us to define the various entries for the page. Since the master pages would not be usually available at the developer machine, designing these pages is not the easiest of task. Did try copying the pages locally to my machine, but VS.NET does not want to pick these, no matter what.
What we would want to do within the initialisation page is to serialize all the user entered stuff and call a Web.Site.WorkflowManager.StartWorkflow with the serialized data. Its this data which the OnWorkFlowActivated event in the WWF workflow would contain (refer to part 2 of this series)
The important points to note here would be the layouts page, the master page , calls to sharepoint functions and the way the page data transfer data to WWF via MOSS.
We have on more VS.NET task remaining, which is creating the task updating page. This page would be used by users to approve/reject tasks. This works a bit different from the three pages listed above; exploits ContentTypes. Next Blog.
There are two more bits we need to do in the WWF workflow application to make it available to MOSS. We need to declare the feature and the workflow xml.
feature.xml
MOSS introduced features for the developers to create site items/functionality which can later be linked with sharepoint collectons/sites. Within this XML file, you would also tell which XML would contain the feature specific details - in our case workflow.xml.
workflow.xml
Describes stuff about our workflow to sharepoint, including name, description , id etc. This also defines the pages which would be used for workflow Instantiation, association and modification. We shall have a look at an example of Instantiation later in a different blog entry. Modification of workflow (say you would to add more reviewers) at runtime needs a few extra steps and this is when the modification page comes into effect.
Defining Custom Pages for Task Initiation
For ease, all of these three pages needs to derive from Microsoft.Sharepoint.WebControls.LayoutsPageBase with sharepoint master pages (~/_layouts/application.master) being used in the ASPX definition. MOSS provides a lot many master pages which give the consistent look and feel of standard MOSS pages. The content placeholders within the masterpages would need to be filled in by us to define the various entries for the page. Since the master pages would not be usually available at the developer machine, designing these pages is not the easiest of task. Did try copying the pages locally to my machine, but VS.NET does not want to pick these, no matter what.
What we would want to do within the initialisation page is to serialize all the user entered stuff and call a Web.Site.WorkflowManager.StartWorkflow with the serialized data. Its this data which the OnWorkFlowActivated event in the WWF workflow would contain (refer to part 2 of this series)
The important points to note here would be the layouts page, the master page , calls to sharepoint functions and the way the page data transfer data to WWF via MOSS.
We have on more VS.NET task remaining, which is creating the task updating page. This page would be used by users to approve/reject tasks. This works a bit different from the three pages listed above; exploits ContentTypes. Next Blog.
Thursday, 5 April 2007
Sharepoint - Integrating MOSS+WWF+ASPX - Part 2
Starting with SharePoint Workflow Project
Once you have the sharepoint extension installed, the default project based on these templates would have the OnWorkFlowActivated activity placed as the first activity in the workflow. When Sharepoint initiates our workflow, its this activity it calls as the first step - acting as the entry point into our workflow.
Data Sharing
Sharepoint shares data with the workflow using one of the following ways:
1.) Instance of SPWorkflowActivationProperties would anytime contain the workflow properties, the most important of which is the initiation data. Initiation data could basically be any custom string passed in while you start the workflow from sharepoint. If you happen to have a custom initiation page (we shall discuss later) which explicitly initiates the workflow within sharepoint, it could perhaps serialise a data class instance based on the form data which we could use via this activation property.
2.) Events - Each of the OnXX events against an activity have event properties passed applicable to the context.
3.) Activity Properties - Each activity have certain properties which map to a workflow property/variable, which you usually setup during design time.
eg:- CreateTask activity has got two properties TaskID, TaskProperty. If you refer to the same variable in a different activity property, (say CompleteTask.TaskID), you are effectively refering to the same task. Also, any data you set to these variables are also passed back to Sharepoint. What you acheive here is sharing/relating data items between activities and between workflow & sharepoint.
Correlation-Token
Nearly all activities have a correlation token which is an identifier for the workflow context. This is for sharepoint to understand the context in which the activity is working. Eg:- you would use the same correlation-token for the createtask, ontaskchanged, completetask activities to specify that all these are of the same workflow context.
Does sharepoint persist the workflow?
Ofcourse it does. There is no way sharepoint can remember workflows running for days (say the task may not have been looked yet by the user). In these special cases when sharepoint is explicitly waiting for an event (during a while activity, during a delay activity etc), sharepoint automatically serializes the workflow. When the related event happens , sharepoint deserializes the workflow and returns control back to the workflow. At this point, its the correlation token which is used to relate the item in concern with the workflow context [eg:- identify the task context]
Example
So to try out some basic stuff, add the basic activities - CreateTaskActivity and within a WhileActivity put the OnTaskChangedActivity and finally a CompletetaskActivity. If your property links and the correlation token is right, there is nothing more to be defined at the workflow definition designer.
To define what happens at each activity (some code finally):
At the CreateTask activity, setup values for the task property like who is the recipient, description, taskid etc.
At the whileactivity, check if the task has been completed - perhaps using a public variable which was inturn set at the OnTaskChanged activity.
And at the completetask, setup the task status to 'complete'.
Guess we are done defining the Workflow.
Next lets define the custom pages which we would use for initiation of the task and also relate these pages to the workflow. Please await for part 3 :)
Once you have the sharepoint extension installed, the default project based on these templates would have the OnWorkFlowActivated activity placed as the first activity in the workflow. When Sharepoint initiates our workflow, its this activity it calls as the first step - acting as the entry point into our workflow.
Data Sharing
Sharepoint shares data with the workflow using one of the following ways:
1.) Instance of SPWorkflowActivationProperties would anytime contain the workflow properties, the most important of which is the initiation data. Initiation data could basically be any custom string passed in while you start the workflow from sharepoint. If you happen to have a custom initiation page (we shall discuss later) which explicitly initiates the workflow within sharepoint, it could perhaps serialise a data class instance based on the form data which we could use via this activation property.
2.) Events - Each of the OnXX events against an activity have event properties passed applicable to the context.
3.) Activity Properties - Each activity have certain properties which map to a workflow property/variable, which you usually setup during design time.
eg:- CreateTask activity has got two properties TaskID, TaskProperty. If you refer to the same variable in a different activity property, (say CompleteTask.TaskID), you are effectively refering to the same task. Also, any data you set to these variables are also passed back to Sharepoint. What you acheive here is sharing/relating data items between activities and between workflow & sharepoint.
Correlation-Token
Nearly all activities have a correlation token which is an identifier for the workflow context. This is for sharepoint to understand the context in which the activity is working. Eg:- you would use the same correlation-token for the createtask, ontaskchanged, completetask activities to specify that all these are of the same workflow context.
Does sharepoint persist the workflow?
Ofcourse it does. There is no way sharepoint can remember workflows running for days (say the task may not have been looked yet by the user). In these special cases when sharepoint is explicitly waiting for an event (during a while activity, during a delay activity etc), sharepoint automatically serializes the workflow. When the related event happens , sharepoint deserializes the workflow and returns control back to the workflow. At this point, its the correlation token which is used to relate the item in concern with the workflow context [eg:- identify the task context]
Example
So to try out some basic stuff, add the basic activities - CreateTaskActivity and within a WhileActivity put the OnTaskChangedActivity and finally a CompletetaskActivity. If your property links and the correlation token is right, there is nothing more to be defined at the workflow definition designer.
To define what happens at each activity (some code finally):
At the CreateTask activity, setup values for the task property like who is the recipient, description, taskid etc.
At the whileactivity, check if the task has been completed - perhaps using a public variable which was inturn set at the OnTaskChanged activity.
And at the completetask, setup the task status to 'complete'.
Guess we are done defining the Workflow.
Next lets define the custom pages which we would use for initiation of the task and also relate these pages to the workflow. Please await for part 3 :)
Tuesday, 3 April 2007
Sharepoint - Integrating MOSS+WWF+ASPX - Part 1
MOSS 2007 brings out the capability to use workflows defined within WWF; though not in the very easiest of ways. The number of steps to perform this action is a bit extensive. Sharepoint by default does let you define simple workflows using the sharepoint designer, but you would definitely have to go via the WWF path if there are too many custom business actions which needs to be performed during the workflow. Think about it, you get the full flexibility of the C# language and the .NET library once you start using the WWF - two steps away from heaven
Stuff you would need to define the workflow
WWF extension to VS.NET - to get the workflow designer and basic workflow project templates.
Sharepoint 2007 SDK - to get the sharepoint workflow templates and activities.
Quickest intro to WWF
Activities interact in sequence (sequential workfow) or via a trigger/state change (state machine workflow) to complete the work-flow. Activities are the building blocks for the workflow; once the WWF extensions are installed, you get a good set of activities to work with - while,if-else,delay,code etc etc.
Custom activities could be defined by the user (check out System.Workflow.Activities) such that these could be plugged into the workflow. While defining workflow, its interesting to note that the workflow definition could be defined in an XML file (XOML file actually) very easily. Its the same XML definition which gets depicted as interconnected boxes in the designer.
Once you have your workflow defined, whats remaining is hosting the same - the easiest option to test a workflow would be to write a console application which initiates the runtime (System.Workflow.Runtime.WorkflowRuntime) and starts the workflow by creating an instance of the previously created workflow using a WorkflowRuntime.CreateWorkFlow. Simple? Try it out... :)
Workflow for Sharepoint using WWF
Once you have the sharepoint SDK installed, you get a two new project templates and custom activities specific to sharepoint like createtask, deletetask, onwtaskchanged etc. We shall use these items in the next session to create a sharepoint workflow and later integrate this workflow into sharepoint.
Until then.
Stuff you would need to define the workflow
WWF extension to VS.NET - to get the workflow designer and basic workflow project templates.
Sharepoint 2007 SDK - to get the sharepoint workflow templates and activities.
Quickest intro to WWF
Activities interact in sequence (sequential workfow) or via a trigger/state change (state machine workflow) to complete the work-flow. Activities are the building blocks for the workflow; once the WWF extensions are installed, you get a good set of activities to work with - while,if-else,delay,code etc etc.
Custom activities could be defined by the user (check out System.Workflow.Activities) such that these could be plugged into the workflow. While defining workflow, its interesting to note that the workflow definition could be defined in an XML file (XOML file actually) very easily. Its the same XML definition which gets depicted as interconnected boxes in the designer.
Once you have your workflow defined, whats remaining is hosting the same - the easiest option to test a workflow would be to write a console application which initiates the runtime (System.Workflow.Runtime.WorkflowRuntime) and starts the workflow by creating an instance of the previously created workflow using a WorkflowRuntime.CreateWorkFlow. Simple? Try it out... :)
Workflow for Sharepoint using WWF
Once you have the sharepoint SDK installed, you get a two new project templates and custom activities specific to sharepoint like createtask, deletetask, onwtaskchanged etc. We shall use these items in the next session to create a sharepoint workflow and later integrate this workflow into sharepoint.
Until then.
Sharepoint BDC - Easy LOB Integration
BDC
MOSS 2007 brings out new capabilities in integrating third party data sources and line-of-business apps into the sharepoint environment. Once integrated, these data sources act 'quite' similar to the standard sources letting you apply the various sharepoint functions such as list, search etc.
There is no need to write complex custom handlers (nobody wants to write one - not recommended by the MOSS team either!) or IFilers, now with the introduction of Business Data Catalog - BDC
The idea is very simple - you could integrate any data source which has an adapter via the ADO.NET path or a webservice. Once you dig out the adapter,the only remaining step is to define the BDC. The BDC definition is an XML file conforming to the BDCMetaData.xsd schema. Tip - when you start with a new XML file using VS.NET 2005, go to the properties for this XML file and point the 'schema' to the BDCMetaData.xsd; this enables intellisense while editing the XML file.
Common items which you define in the BDC :
LobSystemInstance - This defines where the datasource is and the adapter to use for connection, think of it as the connection string you normally provide.
Entities - When you expose the data source, what you definitely need to tell MOSS is what items you need to make available from your datasouce. Say from the default SQL Server [pubs] DB, you might want to expose the employee items only. For each entity, you would need to define the properties (employee id, name etc) and the identifier (employee id) at the bare minimum.
Methods and Method Instances - This defines the actions you could perform against the entity. You would define the method defintion ; say by using an SQL command string with the parameters (parameter types could be .NET types, say System.Data.IDataReader, System.String etc).
Method instances is an interesting concept; the same procedure definition (a template) could have different roles (method instances) to play under different scenario. Method types define the role the method plays. Eg:- when you need to define a SpecificFinder and a Finder type, a single method template should suffice.
Some of the method types defined are quite smart; AccessChecker method type could be used to do a custom access filter of the items in MOSS just before it is shown to the user (say just before the search result is shown within MOSS). You could write stored procedures in the backend which tell MOSS whether the data needs to be shown to the specific user, then link it up as an AccessChecker in the BDC definition file. I think thats cool.
BDCMetaMan - A very handy tool where you define the connection, entities etc visually and the XML file is generated for you. The free version could be used as a draft for more hands on tweaking.
Check out BDC covered extensively at MSDN.
MOSS 2007 brings out new capabilities in integrating third party data sources and line-of-business apps into the sharepoint environment. Once integrated, these data sources act 'quite' similar to the standard sources letting you apply the various sharepoint functions such as list, search etc.
There is no need to write complex custom handlers (nobody wants to write one - not recommended by the MOSS team either!) or IFilers, now with the introduction of Business Data Catalog - BDC
The idea is very simple - you could integrate any data source which has an adapter via the ADO.NET path or a webservice. Once you dig out the adapter,the only remaining step is to define the BDC. The BDC definition is an XML file conforming to the BDCMetaData.xsd schema. Tip - when you start with a new XML file using VS.NET 2005, go to the properties for this XML file and point the 'schema' to the BDCMetaData.xsd; this enables intellisense while editing the XML file.
Common items which you define in the BDC :
LobSystemInstance - This defines where the datasource is and the adapter to use for connection, think of it as the connection string you normally provide.
Entities - When you expose the data source, what you definitely need to tell MOSS is what items you need to make available from your datasouce. Say from the default SQL Server [pubs] DB, you might want to expose the employee items only. For each entity, you would need to define the properties (employee id, name etc) and the identifier (employee id) at the bare minimum.
Methods and Method Instances - This defines the actions you could perform against the entity. You would define the method defintion ; say by using an SQL command string with the parameters (parameter types could be .NET types, say System.Data.IDataReader, System.String etc).
Method instances is an interesting concept; the same procedure definition (a template) could have different roles (method instances) to play under different scenario. Method types define the role the method plays. Eg:- when you need to define a SpecificFinder and a Finder type, a single method template should suffice.
Some of the method types defined are quite smart; AccessChecker method type could be used to do a custom access filter of the items in MOSS just before it is shown to the user (say just before the search result is shown within MOSS). You could write stored procedures in the backend which tell MOSS whether the data needs to be shown to the specific user, then link it up as an AccessChecker in the BDC definition file. I think thats cool.
BDCMetaMan - A very handy tool where you define the connection, entities etc visually and the XML file is generated for you. The free version could be used as a draft for more hands on tweaking.
Check out BDC covered extensively at MSDN.
Subscribe to:
Posts (Atom)