Disabling workflow associations through the SharePoint API

By James Fisk

Recently I was tasked with migrating data from a DB into SharePoint.  Now, the sites that’ll be migrating to have a lot of workflows, and the one requirement was that they needed to be disabled before migration and re-enabled afterwards.  Now we knew how to disable them through the browser and SharePoint designer, but we needed to disable them through the API.  So after a bit of digging around I found that each SPList object in the site has a collection of SPWorkflowAssociation objects.

Below is code that enables and disable work flow associations, however, i discovered when i tried to use the foreach on the SPWorkflowAssociation updating the association causes an error saying you cannot enumerate over this collection because it has been changed.  To get round this I used a normal for loop.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using Microsoft.SharePoint;
   6:  using Microsoft.SharePoint.Workflow;
   7:   
   8:   
   9:  namespace TestWorkFlow
  10:  {
  11:      public class Program
  12:      {
  13:          static void Main(string[] args)
  14:          {
  15:              using (SPSite site = new SPSite("<URL of Site>"))
  16:              {
  17:                  using (SPWeb web = site.RootWeb)
  18:                  {
  19:                      SPList withWorkFlowsList = web.Lists["<List name with work flows>"];
  20:                      if (withWorkFlowsList != null)
  21:                      {
  22:                          SetWorkFlowsEnabledStatus(withWorkFlowsList, false);
  23:                          // do Stuff
  24:   
  25:                          SetWorkFlowsEnabledStatus(withWorkFlowsList, true);
  26:                      }
  27:                  }
  28:              }
  29:          }
  30:   
  31:          private static void SetWorkFlowsEnabledStatus(SPList withWorkFlowsList, bool status)
  32:          {
  33:              SPWorkflowAssociation spWorkflowAssociation;
  34:              for (int i = 0; i < withWorkFlowsList.WorkflowAssociations.Count ; i++)
  35:              {
  36:                  spWorkflowAssociation = withWorkFlowsList.WorkflowAssociations[i];
  37:                  spWorkflowAssociation.Enabled = status;
  38:                  withWorkFlowsList.UpdateWorkflowAssociation(spWorkflowAssociation);
  39:              }
  40:          }
  41:      }
  42:  }

categoriaSharePoint commentoNo Comments dataAugust 6th, 2010
Read All

Using Typemock VS2010 Beta 2

By James Fisk

After installing TypeMock with VS2010 Beta 2 I found there was no integration in VS2010.  This means that when you run your unit tests in your integrated unit test runner TypeMock would not kick in.

To workaround this use TMockRunner.exe with nunit as an external command like this :-

"c:\Program Files\Typemock\Isolator\5.4\TMockRunner.exe" "c:\Program Files (x86)\NUnit 2.5.3\bin\net-2.0\nunit-console.exe” "c:\Projects\Aberdovey2010\spikes\UnitTesting\Sample.Tests\bin\Debug\Sample.Tests.dll"

You will need to change the paths for your environment.

As mentioned this will need to executed outside the outside Visual Studio from batch file, or you could add the command as an external tool.

To do this got to menu Tools->Externel Tools…

This will bring up the following dialog box :-

externeltools

Enter the title, location of the command, in this case TMockRunner.exe, and the arguments which should be, <nunit install path>nunit-console.exe <full path to TEST assembly>.  Don’t forget to check the ‘User Output to window’ check box, which means all output from this command will go to the output window, simples.  Have fun.

categoriaSharePoint, typemock commentoNo Comments dataJanuary 6th, 2010
Read All

Mixing JQuery, SP2010 Client OM with the sand pit

By James Fisk

On a recent project we had set a requirement for the solution to run as a SharePoint 2010 Sandboxed solution,  and needed to provide a much richer UI experience, so we decided to use JQuery and Client OM.

This presented me with few challenges, what helped was a deep dive into SharePoint 2010 with the first ever Combined Knowledge 2010 beta training course with Todd Bleeker and Gary Yeoman.  Because I follow agile practices, I spiked this, what I mean by this is that a spike is a term used for researching a way of doing something, so that the team can estimate the story that requires this approach.  After I completed my spike we were in a much better place to estimate the story, this post is my journey in trying to understand this problem.

Here is the code that this post discusses, please bear in mind that this is not production code.

First of all i set up myself with a VMWare image of SharePoint 2010 running on Windows Server 2008 SP2.  I used these Instructions from Microsoft.

Once i got myself setup I then researched how I would go about making this happen. After googling for what seemed like and eternity I stumbled across this post from Lighting Tools, which enabled me to load javascript in a sandboxed solution.

So I set to work. I created a blank SharePoint 2010 project in Visual Studio 2010 and added a web part, don’t go for the visual web part they are not compatible with sandboxed solutions.

Don’t create a Visual Web Part in sandboxed solution as they are not compatible, mainly because visual web parts need physical files on the file system.

You then add this snippet of code to the web part cs file, as detailed in the post I mentioned earlier.

   1:          protected override void RenderContents(HtmlTextWriter writer)
   2:          {
   3:              string url = SPContext.Current.Site.RootWeb.Url;
   4:              writer.Write("<script language='javascript' type='text/javascript' src='" + url + "/SiteAssets/Assets/Start.js'></script>");
   5:              writer.Write("<script language='javascript' type='text/javascript'>Sys.loadScripts('" + url + "/SiteAssets/Assets/jquery-1.3.2.min.js');</script>");
   6:              writer.Write("<script language='javascript' type='text/javascript'>Sys.loadScripts('" + url + "/SiteAssets/Assets/jquery-ui-1.7.2.custom.min.js');</script>");
   7:              writer.Write("<script language='javascript' type='text/javascript'>Sys.loadScripts('" + url + "/SiteAssets/Assets/SandBox.js');</script>");
   8:          }

Notice the use of SPContext.Current.Site.RootWeb.Url, this is to ensure that the script always loaded from the root web, regardless of where the webpart running from.

This code is loading a javascript file (Start.js) that is from Microsoft’s AJAX library.  This loads all the required functionality, such as the Sys namespace.  I could have used the ‘Sys.require’ to load the required JQuery, but I wanted tight control over that instead.

Next the real magic happens, using the Sys.loadScripts function you ensure that one, and only one copy of the script you have selected is being loaded.  Notice that the script are being loaded from a folder called /SiteAssets/Assets.  This is a container in SharePoint that stores the script into the content database, very important if you want to use a Sandboxed solution.

Next I created a normal sitepage in SharePoint, which I used to load the web part and javascript.  Once the site page was created I used SharePoint Designer 2010 to get hold of the contents of the page, it was easier that way because I’m lazy at typing, but you can use whatever method you want.

Remember the SiteAssets/Assets folder in the web part that loads the jquery and other javascript files.  For this to work you need to create a module called ‘Assets’ and added all the required files for the module.  Don’t forget to add the ‘URL’ attribute to the Elements.xml file to ensure the files get rendered from the correct location.

siteassets

Now, I created another module, this time it is for the deployment of the sitepage that will contain the javascript loading web part, and your html that the javascript will interact with.  In my case it was to mimic a white board that SCRUM teams use.  In this UI you can drag and drop the story boxes from one swim lane to another.  Dropping the story into a swim lane would update the stories’ status, this being ‘Not Started’, ‘In Progress’, ‘Verify’ or ‘Done’.  The story status is stored in a SharePoint list.

Each swim lane is a div, which i had to mark as droppable by using jquery UI plugin.

   1:      $("#notStarted").droppable({
   2:          drop: function (event, ui) {
   3:              updateStoryStatus('notStarted', ui.draggable);
   4:          }
   5:      });

These 6 lines does quite a lot.  I’m selecting a div with the ID ‘notStarted’, you use the ‘#’ notation to depict an ID.  Then I enable the div to become droppable,that is to accept any draggable item.  Droppable can handle upto five events, these being ‘activate’, ‘deactivate’, ‘over’, ‘out’, ‘drop’.  For this I just concentrated on the event drop. When the event fires it uses an anonymous function, which then passes control over to another function that updates the stories’ status.  The first parameter is obviously the status I want to set the dropped story, the second parameter is the object that was dropped, this is supplied by this rather curios syntax ‘ui.draggable’.  This happens in the initialisation stage.

Initialisation stage

In the SandBox.js file I have all the custom code.  This code handles the loading of all the stories from the story list on SP2010 and updated story status based on where the stories are dropped.

Now, one of the very first functions i call is the following :-

ExecuteOrDelayUntilScriptLoaded(start, "sp.js");

This line is very important, the reason why is that in order to use Client OM in javascript, you need the JS file ‘sp.js’.  This JS framework allows me to load current SP context and start to query and update the SharePoint Lists and so forth.  It is a limited framework that can only talk back to the SP server farm from whence sp.js is loaded from.  This is for obvious security reasons.  You might be wondering what the function ExecuteOrDelayUntilScriptLoaded is for.  This function is used to ensure the that the function ‘start’ (starting point of my app) is only called when the file ‘sp.js’ is loaded, if not, then it can’t be guaranteed when the client om framework is loaded.  Incidentally, the function ExecuteOrDelayUntilScriptLoaded should not be in a function, you want it to be called as soon as the ‘sandbox.js’ is loaded.

In order for the ExecuteOrDelayUntilScriptLoaded function to work you need to load the JS file this function is in the ‘init.js’, that is part of the ‘sp.js’.  For the ‘sp.js’ to be loaded you need to include it into the ASPX page, like so :-

   1:  <script type="text/javascript">
   2:          <SharePoint:ScriptLink language="javascript" name="sp.js" LoadAfterUI="true" runat="server"/>
   3:  </script>

When the ‘Start’ function is called all the swim lanes are set as droppable, then the stories are loaded.

Loading the stories

This is where client OM really comes in to it’s own.  Knowing I now have ‘sp.js’ loaded and ready I can start using client om.  The first thing I had to do is load the context :-

context = new SP.ClientContext.get_current();

Now I have the equivalent to SPContext on the client side, now to load the story status list :-

    this.site = context.get_web();

    context.load(this.site);

    lists = site.get_lists();
    displayAStory(lists.getByTitle('StoryStatus'));

Here I grab the current web, load that into the main context, retrieve all the lists, then retrieve the list called story status passing it to a function load displays all the stories and places them into the correct swim lane.

Display the stories

Once the correct list has been loaded all that remains is to load all the list items :-

   1:  function displayAStory(currentItem) {
   2:      var camlQuery = generateCAMLQuery('<View></View>');
   3:      this.listItems = currentItem.getItems(camlQuery);
   4:      context.load(listItems);
   5:   
   6:      context.executeQueryAsync(
   7:          Function.createDelegate(this, this.onQuerySucceeded),
   8:          Function.createDelegate(this, this.onFail));
   9:  }

First I needed to generate a CAMLQuery to put against the story list.  The list items are then filtered against the CAML query, load that list into the context, finally execute the loading of story list asynchronously.  With executeQueryAsync you supply two functions, the first one is called when the execute command was successful, the second is called when the command fails.

Successful ASync command

After the command has been executed and it was success, this function is called as a delegate by the  executeQueryAsync method mentioned earlier :-

   1:  function onQuerySucceeded(sender, args) {
   2:      var itemCount = listItems.get_count();
   3:      for (i = 0; i < itemCount; i++) {
   4:          var item = listItems.itemAt(i);
   5:          if (item.get_item('Status') == "Not Started") {
   6:              $("#notStarted").append(generateStoryDivHTML(item));
   7:          }
   8:          if (item.get_item('Status') == "In Progress") {
   9:              $("#inprogress").append(generateStoryDivHTML(item));
  10:          }
  11:          if (item.get_item('Status') == "Verify") {
  12:              $("#verify").append(generateStoryDivHTML(item));
  13:          }
  14:          if (item.get_item('Status') == "Done") {
  15:              $("#done").append(generateStoryDivHTML(item));
  16:          }
  17:      }
  18:      $(".story").draggable({cursor:'crosshair'});
  19:  }

Here I simply loop over all the items in the list, looking at the status of the current story and placing it into the corresponding swim lane.  The story is generated by creating a div.

Generating a Story box

To generate a story box I simply generate a DIV on fly inserting the story name.

   1:  function generateStoryDivHTML(item) {
   2:      return "<div class='story'><span id='StoryTitle'>" +
   3:              item.get_item('Title') +
   4:              "</span><input type='hidden' id='itemId' value='" +
   5:              item.get_id() +
   6:              "' </div>";
   7:  }

The story id is also added, but as a hidden field, this is used when the app wants to know which story should be updated after it has been dropped.

Update story status

In order to update the story I had to store the story ID in each story div, so that when the ui.draggable object is supplied I can grab the story id from a hidden input field here is the code that does that :-

   1:  function updateStoryItem(storyElement, newStatus) {
   2:      var storyId = storyElement.find('#itemId').attr('value');
   3:      var itemToUpdate = listItems.getById(storyId);
   4:      itemToUpdate.set_item('Status', newStatus);
   5:      itemToUpdate.update();
   6:      context.load(itemToUpdate);
   7:      context.executeQueryAsync(updateStoryStatusSucceed, onFail)
   8:  }

In here I grab the story id from the hidden field and then use it to grab the list item by it’s id.  Then I take the ‘newStatus’ and update the item by column ‘Status’ with the ‘newStatus’.  All that remains is to update the list, then load it into the current context and execute Async on the context in order to  persist the changes.

Final thoughts

I know this has been a whirl wind overview of using jquery, client om in a sandboxed solution, and no doubt there might be a better way of achieving the same thing, but I’m still learning.  Using AJAX based web apps is now easier in SP2010 with Client OM.

Speed might be an issue I have not done any performance testing on this, so that might raise a few issues.  On the whole I hope this will help in some way to understand Client OM with Javascript.

categoriaJQuery, SandBox, SharePoint, research commento2 Comments dataDecember 26th, 2009
Read All