SharePoint folder creation through code

  • 30 April 2013
  • 7 replies
  • 2 views

Badge +5

I've got a simple approval workflow that uses an InfoPath form through SharePoint. The last step of the workflow saves the xml to the form library for record-keeping. The requirement is that these forms are stored in a folder for the current year.


In my process I added a SharePoint document event to create that "2013" (used the year function) folder, and the document will save there appropriately the first time. The problem is that the next time the workflow runs, it fails because that folder already exists.


My question should be pretty simple; I'm assuming I'll have to use code to create the folder, checking to see if it already exists. I haven't had much luck searching for that code or scenario. Does anyone here have an easy way to achieve this? I'd also be open to alternatives without using code.


Thanks in advance.


7 replies

Badge +8

This is something that will change when the SharePoint 2013 components are released. The new behavior will be to create Folder if it doesn't exist.


In the meanwhile, there is a way around that, but requires some code. Your default route does not create the Folder, it expects it to be there. Then, the process exception rule, you need to write code for the exact error message that is raised when trying to upload a document to a nonexistent folder. If this happens, ignore the error and set a variable, let's say CreateFolder for argument's sake, to true. CreateFolder should also have a default value of false.


Subsequently, the activity that does the upload has two line rules. One that checks for the CreateFolder == true, one for CreateFolder == false. The CreateFolder == true will then route to an activity that creates the folder and then reroute back to the upload activity, after resetting the CreateFolder to false. CreateFolder == false will follow the normal route. This way, one expects the error only once a year.


Disclaimer, I have not tested the code, make sure it works as expected for your scenario:


private void Properties_ExecuteCode(object sender, EventArgs e)
{
    Exception ex = (Exception)K2.ExceptionObject;
    if (ex.Message == "Your Message Here")
    {
        K2.AddToErrorLog = false;
        K2.AddToServerLog = false;
        K2.ProcessInstance.DataFields["CreateFolder"].Value = "true";
    }
    else
    {
        K2.AddToErrorLog = K2.Configuration.IsErrorLog;
        K2.AddToServerLog = K2.Configuration.IsServerLog;
        throw ex;
    }
}


And the pseudo process:


Badge +5

dc,


This is great, thank you for the response.  This almost worked, but I'm obviously missing something.  When I tried to catch that error the ex.Message that was returned was "WorkflowTerminated". It seemed too generic to me, but I gave it a try anyway. The first one that went through created the folder properly, but got stuck in an infinite loop.


The next step is a completion email and the user got hundreds of them. I guess my next questions would be, is there a specific log I should check to see the real error message and is there a way to prevent loops?


Here is what my code looks like now:

Badge +5

Here is the code, it was formatted horribly in my last post.


private void Properties_ExecuteCode(object sender, EventArgs e)
        {
            /*K2.AddToErrorLog = K2.Configuration.IsErrorLog;


            K2.AddToServerLog = K2.Configuration.IsServerLog;*/
            Exception ex = (Exception)K2.ExceptionObject;
            if (ex.Message == "WorkflowTerminated")
            {
                K2.AddToErrorLog = false;
                K2.AddToServerLog = false;
                K2.ProcessInstance.DataFields["CreateFolder"].Value = "true";
            }
            else
            {
                K2.AddToErrorLog = K2.Configuration.IsErrorLog;
                K2.AddToServerLog = K2.Configuration.IsServerLog;
               
                K2.ProcessInstance.DataFields["CreateFolder"].Value = ex.Message;
                throw ex;
            }
        }

Badge +5

I've found that you can stop a looping process by stopping the K2 server service and making a change in the database. In the K2.Server.ProcInst table, set the Status column for the looping instance to 4. That should stop it. You can then restart the service and remove the process through the management console.

Badge +10

You might already have this all figured out and the approach taken suggested in the earlier threads should work fine as well. 


A slightly different approach would be to create a "Process Reference" to the SharePoint DWS Web Service Create Folder method which doesn't generate an error when a folder already exists e.g. http://portal.denallix.com/_vti_bin/dws.asmx?WSDL


Then in a default server event add code similar to the following  to call the Create Folder method of the DWS service:


 


 


 




CreateFolder.



 



 


Dws CF = new CreateFolder.Dws

();

CF.Credentials = System.Net.



 



 


CredentialCache

.DefaultCredentials;

K2.ProcessInstance.DataFields[



 



 


"Folder Name"].Value = "Shared Documents/" + K2.ProcessInstance.DataFields["Folder Name"

].Value.ToString();

CF.CreateFolder(K2.ProcessInstance.DataFields[



 



 


"Folder Name"

].Value.ToString());



Badge +8

I think the problem with your process is that you need to reset the CreateFolder datafield in the Activity that creates the folder (it should be false in order for the process to continue). I would also remove the K2.ProcessInstance.DataFields["CreateFolder"].Value = ex.Message; line in your else statement. The "CreateFolder" datafield should be a Boolean.


With regards to your comment on the generic error message, you can create a test variable (let's say FolderCreateFlag with a default of false) which you can set to true in the event just before the upload and set to false directly after. That way you have two flags to test:


if (ex.Message == "WorkflowTerminated" && Convert.ToBoolean(K2.ProcessInstance.DataFields["FolderCreateFlag"].Value))
            { .....

Badge +5

dc,


I started over this morning, and it appears to be working now. Obviously I missed some detail the first time through.


Thank you again for your help with this.

Reply