Showing posts with label Workflow. Show all posts
Showing posts with label Workflow. Show all posts

Wednesday, 26 September 2007

Infopath Development Tips 1 - Infopath repeating table manipulation

Infopath forms are the essential part of sharepoint workflow development. Repeating table is also very tempting because it can solve many UI problems when presenting group of data. So the problem is , how do we populate repeating table from the data we have. It's easy. AppendChild method of DOM using managed code and Infopath's powerful conditional formatting.
1. First thing first, let's specify the conditional formatting for repeating table. right click the table, then properties->conditional formatting-> add. Then you can specify when the value of element of table, say , text box is blank, tick "hide this control". This will make sure your table doesn't show extra blank row when inserting the rows. We will see why we might need to hide this row later on.

2. Secondly, we need to use a little code behind to populate the table we have in the IP form. Say the name of the table we have is called docList and the collection of data we have is docArray. so the code will be



//repeating table

relDocListTable = mainRoot.SelectSingleNode("/my:myFields/my:docList", this.NamespaceManager);


//repeating table row
relDocListRow = mainRoot.SelectSingleNode("/my:myFields/my:docList/my:docItem", this.NamespaceManager);

for (int index = 0; index < newrow =" relDocListRow.Clone();" innerxml =" docNameArr[index];">

Wednesday, 12 September 2007

Sharepoint Development Tips 6 - Developing Visual Studio Workflow for MOSS

pitfall 1:

there's a good article (well, for starters ) on msdn you guys can read to start your very first workflow using Visual Studio. But after firing up VS 2005 and ready to go with the instructions of this article, you found there's no onworkflowactivated1 as your necessary starter of the dish... so basically that's how the init data will kick in from MOSS and your workflow won't work without it. Even if you add those 3 missing references already and are able to see the chart , it will be the same.

Solution: Don't trying to be clever, install ECM kit on your development server with sharepoint on, copy around the dll files needed by your project is not good enough this time.

pitfall 2:

The snippet is essential to have when completing your workflow.xml and feature.xml in order to install your workflow and all related Infopath forms. Rather than google on the Internet, as we know, MS provides this snippet coming with the ECM kit. But when you press ^K+X, the WSS snippet described in MSDN is not there.

Solution:
Serge Luka's blog did save us a lot of time to tell us where this snippet is. Simply press ^K+B to navigate to the path of "C:\Program Files\Microsoft Visual Studio 8\Xml\1033\Snippets\Windows SharePoint Services Workflow" and add the snippet code into your code snippet manager. There should be another one for MOSS but the workflow.xml and feature.xml should be the same.

pitfall 3

SendEmail activity always seems tempting when developing workflows using Visual Studio because email can be a very important process. While a lot of people complaining the activity does not do what it is supposed to even though there's no error in event viewer of Windows or ULS log of MOSS. Can it be security issues or central admin's outgoing email settings, etc... confusing

Solution:
Wake up, the MailMessage utility in System.net still works as well as in .net 2. If you know the smtp server's address , this can be perfect to use in this scenario. Anyway, even if SendEmail Activity works, you still have to specify the to, from, and body so on to make it send emails you want. MailMessage object that can be sent by SmtpClient is easier to handle. Just drag a code activity instead and put your SmtpClient+MailMessage inside the handler of that code activity. It's guaranteed working if you get your smtp outbound server's address.

Thursday, 5 July 2007

Sharepoint Development Tips 4- SPD workflow madness

As I introduced SPD workflow in my previous post,if all you need is simply customized activity and it's supposed to be stand-alone, i.e. doesn't need to be reused to another list in sharepoint, and you certainly don't want the hassle with init,complete forms with InfoPath, customized activity/condition with SPD's workflow designer is almost perfect choice. But when any offer looks too good to be true, it probably just is. Like the line in Supernatural, "This world just ain't coming without perks...", well, in the bad way in our case of course !-_-...

The catch is when you want to update your code using SPD, you will find the change you made will not be used by sharepoint. And as you come this far with it already, you know there's NO debugging support with SPD workflow. You really want to make sure it "somehow" just works anyway before you deploy it to the server. So how on earth we can make our updated dll work with the SPD workflow on the server? Well, you will have to live with the fact that you can't debug given the fact your dll will not contain huge amount of code. But to update the dll on the server, do the following.

1. Remove the workflow from your list in sharepoint
2. Shut down your SPD
3. Go to your website cache, on my XP pro the path for this is : "C:\Documents and Settings\%user%\Local Settings\Application Data\Microsoft\WebsiteCache", in vista , it's under different path:%System Drive%\Users\%user%AppData\Local\Microsoft\WebSiteCache(You will find it's a mistake to use Vista for professional development for now anyway). Delete everything you see there.
4. Copy the updated dll to the GAC on your server
5. IISRESET on your server
6. reopen SPD
7. Redeploy your workflow.


In this way, you will update your SPD workflow with latest dll. But I have run into the occasions that even above method will not update your dll. I ended up restarting the sharepoint server to get it updated(too much for a real-world software solution , is it?). Hopefully this will help you.

Update:
now every time I have to restart the sharepoint server to make changes. :(

Friday, 8 June 2007

Customize Sharepoint Designer Workflow activity

Document management is the soul of all sharepoint project which is content management based or simple intranet application. Sharepoint Designer 2007 shipped along with MOSS is a powful tool which allows greater user involvment to customize the site and more importantly, build customized workflow without much coding effort. This shares the same thought with Expression Web that complies with .net 3.5 technologies which web designers can accomplish the tasks that had to be done by developers. This following piece of text will demonstrate how to use sharepoint designer's workflow designer to augment the existing actions provided by WSS 3.0.





As said above, sharepoint developers will run into implement document handling across different list through the lifecycle of document management whatever the project is like. Copy or move file items /list item from one to another according to different event (document change, document created etc.) is almost a must-have functionality. Workflow built in sharepoint designer can do this without a single line of code.









and if you want to move item, simply add another action "delete item in this list".

However, almost certainly you will want to have more actions or conditions when building the workflow in realworld. For example, above action will only copy the item to the list's root folder, what if you want to add it to certain folder or create one if not exist? This is where the customized workflow comes in, there are two alternatives: Customized Workflow created by Visual Studio and deployed as feature, this kind of workflow will not be restricted to certain list and therefore reusable;Workflow created by visual studio but deployed by using sharpeoint designer. This one will be tied up with certain list and not as reusable, yet easier to implement & deploy. This post will introduce how to create sharepoint designer workflow custom activity using based upon the copy item to folder action.








  1. First of all, you will need to install Visual Studio 2005 Extensions for WF, after that, you will have the work flow library template for you to start build your new project on. Give it a name, let's call it "DispatchActivity"


  2. On the design view of the workflow, drag and drop a code activity for your first and only activity.from your toolbar, give it a name of "CopyListItemAct"


  3. Double click the code activity to edit the code-behide , the CopyListItemAct_ExecuteCode event handler will be one the code to be executed in this step. You can find the event wire-up in InitializeComponent() as expected in designer.cs.

  4. Find the Microsoft.sharepoint.workflowactions.dll in ISAPI folder in your MOSS's c:\program files\common files\Microsoft shared\web server extension\12\ISAPI folder, and add it to your project as reference. The workflow context property will need it later


  5. A SPD activity or a condition needs a textual descrition to represent the literal meaning of action, which is called a "sentence". In our case, the sentence will be "Copy this Item in one list to a folder in another list". The bold keywords will be the properties that are needed in our custom activity. So in order to "promote" these properties, we need to have properties called DependencyProperties which is necessary for promoting properties to SPD workflow. This is a requirement from windows workflow foundation, See MSDN for more information.


    So by looking at the sentence that is needed in SPD, we need to have the context of sharepoint so we can get the list item and list objects from the site. Also, we need to have the reference of current listitem and the target list. So the dependency properties will look like following.




    //current sharepoint context
    public static DependencyProperty __ContextProperty =
    DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(DispatchActivity));
    [Description("context")]
    [ValidationOption(ValidationOption.Required)]
    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public WorkflowContext __Context
    {
    get { return ((WorkflowContext)(base.GetValue(DispatchActivity.__ContextProperty))); }
    set { base.SetValue(DispatchActivity.__ContextProperty, value); }
    }

    //list id
    public static DependencyProperty ListIdProperty =
    DependencyProperty.Register("ListId", typeof(string), typeof(DispatchActivity));
    [Description("List Id")]
    [ValidationOption(ValidationOption.Required)]
    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public string ListId
    {
    get { return ((string)(base.GetValue(DispatchActivity.ListIdProperty))); }
    set { base.SetValue(DispatchActivity.ListIdProperty, value); }
    }

    //list item
    public static DependencyProperty ListItemProperty =
    DependencyProperty.Register("ListItem", typeof(int), typeof(DispatchActivity));
    [Description("List Item")]
    [ValidationOption(ValidationOption.Required)]
    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public int ListItem
    {
    get { return ((int)(base.GetValue(DispatchActivity.ListItemProperty))); }
    set { base.SetValue(DispatchActivity.ListItemProperty, value); }
    }

    //TolistId item
    public static DependencyProperty ToListIdProperty =
    DependencyProperty.Register("ToListId", typeof(string), typeof(DispatchActivity));
    [Description("Target List Name")]
    [ValidationOption(ValidationOption.Required)]
    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public string ToListId
    {
    get { return ((string)(base.GetValue(DispatchActivity.ToListIdProperty))); }
    set { base.SetValue(DispatchActivity.ToListIdProperty, value); }
    }



  6. These properties will simply be used as normal c# properties.

  7. Before we start coding on CopyListItemAct_ExecuteCode to copy item over to the folder in the target list, we need to create a .ACTION file to map all the dependency properties in the class so Sharepoint can recogize the custom sentence and present the sentence to the user to fill it in . The xml file should look like as following.


    What is worth mentioning is that you should always keep in mind putting the __context property in front of other properties, either in ACTIONS file or in your class. I will explain this later. Also, in the first fieldBind, we notice that we should use the format string of "ListId,ListItem" to get the current item in the current list. The result in SPD will be a dropdownbox prompting to confirm/select other list items to copy from.




  8. Now we code the CopyListItemAct_ExecuteCode() in your activity .cs file for folder create and copying activity.

    private void CopyListItemAct_ExecuteCode(object sender, EventArgs e)
    {
    try
    {
    /*setup context and get reference
    of current list SPD is attached to
    and the item we are dealing with*/
    SPSite thisSite = __Context.Site;
    SPWeb thisWeb = __Context.Web;
    SPList thisList = thisWeb.Lists[new Guid(this.ListId)];
    SPListItem thisItem = thisList.GetItemById(this.ListItem);
    //get the target list
    SPList targetList = thisWeb.Lists[new Guid(this.ToListId)];

    /*e.g. we can use the name of curent item to create folder
    in target list*/

    if(targetList !=null)
    {
    string folderName = thisItem.Text;
    //check if the folder exist in target list
    if (!ExistingOrder(folderName, targetList))
    {
    //create a folder with the folderName in the target list
    string listURL = thisWeb.Url+"/" + targetList.Title;

    //the folder in Sharepoint can only be created this way
    //by specifying the leafname as the folder name
    SPListItem newFolder = targetList.Items.Add(listURL.Trim(), SPFileSystemObjectType.Folder, folderName);

    if (newFolder != null)
    {
    //reflect the change
    newFolder.Update();
    targetList.Update();


    //copy item over to the folder we just created
    foreach (SPListItem targetItem in targetList.Folders)
    {

    if (targetItem.Folder.Name == orderNumber)
    {
    AddFileToFolder(thisItem.File, targetItem.Folder);
    }
    }
    }


    }
    else // if the folder is there already, copy item
    {
    //copy item over to the folder we just created
    foreach (SPListItem targetItem in targetList.Folders)
    {

    if (targetItem.Folder.Name == orderNumber)
    {
    AddFileToFolder(thisItem.File, targetItem.Folder);
    }
    }
    }
    }




    }
    catch(Exception)
    {...}
    }




    Then we code the AddFileToFolder method, which is only couple of lines of code, breezy...

    private void AddFileToFolder(SPFile _thisFile, SPFolder _thisFolder)
    {

    string fileURL = _thisFolder.Url + "/" + _thisFile.Name;
    try
    {
    byte[] fileByte = _thisFile.OpenBinary();

    _thisFolder.Files.Add(fileURL, fileByte);

    }
    catch (IOException ioe)
    {
    throw new IOException(ioe.Message);
    }

    }

    As for how to check if the folder exists, the code is very straightforward, it will not to be posted here to save space.


  9. After signing your assembly, we need to make sure it is deployed correctly.

    1. After you make the ACTION file, make sure you put it either in a independent xml file with extension of .ACTIONS under the server's folder %SYS_VOL%:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\1033\Workflow, or simply just copy the part of elements to the WSS.ACTIONS file under the same folder as above. The reason why I said that the __context should go first is that sharepoint will automatically change your properties order in ACTIONS file, while making sure it's the same order as the one in your activity class file, the __context always comes the first. (This is true when you put your custom activity in WSS.ACTIONS, never tried to prove if it's true in separated file. )
    2. In your web.config file, you need to specify your assembly is an authorized control as others in Sharepoint, you can achieve this by adding in following line
    in,


    You will need to make sure above is 100% correct against your assembly name and pubilc key, otherwise, you might find out that the action is not showing up in workflow designer when you try to add it to the SPD action in steps.


  10. Last thing to do is to deploy the dll you made to the GAC, and do an IISRESET so the sharepoint will pick up your new workflow activity.



Now if you open up Sharepoint designer, when you try to add in your action, you can click "more actions"->choose "Custom SPD Activities" in the category dropdown , you should be able to see your own custom activity if everything in this article is followed. Good Luck!