Tuesday 27 September 2011

SharePoint Designer 2007 Workflows Are Not Portable. At All. No Matter What It Says.

SharePoint Designer lied to me last week. I had a SharePoint list on a site and the user of the site wanted to rename the library upon which I had compiled the workflow. In Designer, you have the ability to create workflow on a list - a bit like event handlers but without code involved - and I went in to have a look at the one I'd just made. On the first screen, where it says the List Name, it appeared that it had changed the name in the workflow just as the user had on the site. I opened it and clicked Finish to re-run it. No errors. Happy days, everything was working fine.

Until I got an email saying it wasn't working. Cue much embarrassment on my part, apologies, and then the one thing I had thought I might avoid - deleting the workflow and recreating it all over again. But really the take home message from this experience is this:

Whenever you need to move or alter a workflow in Sharepoint Designer 2007 because of a change to the site or list, you need to recreate it again from scratch.


This is because Sharepoint Designer retains a hard-coded memory of those blasted GUIDs. You can never use the workflow for any other list, you can't move it anywhere, and as I have just learned, you can't rename the list. Oh and forget trying to wrap it up in a template file in .stp format - it won't work.

And it won't bother giving any errors. Errors are for wimps.

The second take-home message would be - Whenever you regenerate the workflow, create a new item on the new list. Yes, even if the list is "live". You can always delete it. Just don't trust Sharepoint Designer 2007 to tell you anything!

Saturday 24 September 2011

SPSecurity.RunWithElevatedPrivileges Doesn't Work: The Pleasures and Sorrows of the Non-Admin User

I blogged the last time about deploying event handlers using features and components. So your event handler is sitting there, ready to fire. You update, add, or delete or whatever it is you want to do. Nothing happens. You have a look at the feature to check all is well. No typos? No copy-and-paste issues with the component name? Got the right list template ID? Instrumented your error code and recording nothing?

Then you look at the top right corner of your site and see the tell-tale message "Logged In As Serf Minion". You log out and log back in as "God of All Admins" and hey presto, everything works fine. Only problem being that you don't want every user in the damn system having admin privileges on this site or list or document library because - well you just don't. That way madness lies.

So what do you do?

Well I had this problem just a week or so ago. Since I am a novice, I did a bit of googling and came across the function SPSecurity.RunWithElevatedPrivileges. You pass in a delegate, which is a fancy word for an entire wodge of code, and all that code runs as if you're logged in as an admin. Hurrah, thought I, my problem is solved. And gentle reader, I took care to read the warnings. It is bad practice to instantiate your SPSite and SPWeb objects outside the RunWithElevatedPrivileges block. Why? Well, what is the point of creating the admin code block when you already have your site object instantiated as Serf Minion when firing the event handler? Bit of a waste of time that.

So what you're supposed to do is get your site Guid from your existing properties object and then create your site within the delegate block, treating the whole thing like a bubble. Thusly:


Guid siteID = properties.SiteID;
Guid webID = properties.ListItem.Web.ID;


SPSecurity.RunWithElevatedPrivileges(delegate() {
//create site
          using (SPSite site = new SPSite(siteID))
          {
              using (SPWeb web = site.OpenWeb(webID))
                   {
                         //insert godly admin code here with no reference to properties object
                   }
          });

So I tried that, fair readers, or something very similar. Guess what? My code, which had been failing for non-admin users, now failed for everybody. I tried everything in the book to make it work. I could not figure out what the hell I was doing wrong! Then a thought suddenly struck me: maybe this is just a load of crap. So I googled "Run With Elevated Privileges doesn't work" and came straight back with this link from a Mr Ishai Sagi and all I can say it, I wish to God I'd found it sooner. I googled further to find the code somebody had written to create an Impersonation class (Lesson no. 1 - NEVER code something before checking someone else has done it on the internet.) Replaced the settings with my own server settings, ran the thing and HEY PRESTO IT WORKED.

So lesson learned: always test as non-admin and keep in mind non-admin privileges when writing your event handler. And the hell with RunWithElevatedPrivileges where event handlers are concerned. Sorry Mr Microsoft guys, I just couldn't make it work ::sniff::

Wednesday 21 September 2011

Event Handlers - How I Do Them - Part 2 - Features

If all has gone well, we have a compiled dll sitting in the GAC of the machine where Sharepoint lives. However SharePoint does not have a bull's notion that this dll exists so it cannot execute it on the required event. In order to hook up the two parties, we need to create a Feature.

In SharePoint proper, features live in the page known as Site Features in the Site Settings screen. Click on that and generally you will see a list of features. Click Activate and these get Activated. What we want to do is add in our feature so that SharePoint can see it too.

This requires an introduction to that fearsome region known as the Twelve Hive.

The Twelve Hive is a nickname for the forest of folders which contains all the SharePoint system stuff and web configs and the like. Tamper with these at your peril. The most irritating thing about this Twelve Hive is that all the files you need are neck deep in the midst of long folder paths. The system paths will vary but generally to find the features, one should navigate to

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\FEATURES

Create a new folder with your event handler name in the Features folder. It doesn't really matter if you call it Mickey Mouse, to be honest, but it helps for clarity when you call it from the command line later. Then you need to create two xml files: Feature.xml and Elements.xml. To save you tearing your hair out, here are two samples I made earlier:



Feature xml:

<?xml version="1.0" encoding="utf-8"?> <Feature Scope="Web" Title="Event Handler of uncertain provenance" Id="CD603E5F-0148-4ce0-B4EC-FB0ECB138A03" xmlns="http://schemas.microsoft.com/sharepoint/"> <ElementManifests> <ElementManifest Location="elements.xml"/> </ElementManifests> </Feature>

A brief explanation: Scope="Web" is what is most useful when working with events on document libraries, lists and other objects that exist in the SPWeb object scope. Scope = "Site" is only useful when programming site event handlers e.g. stuff that happens when you create a subsite. A bit like UHT milk, as Pat Mustard might have noted. Observant folk might ask: what's that long streel of a thing in the ID field and where did she get it from? Answer is that it's a GUID and you can get one by going into Visual Studio, selecting Tools menu and Create Guid. Simple as that. Oh and don't use the one in the sample - you need to get your own for each feature. That's how Sharepoint keeps track of them. It loves GUIDs.

Elements xml:
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Receivers ListTemplateId="100">
    <Receiver>
      <Name>[YourDLLName]</Name>
      <Type>ItemAdded</Type>
      <SequenceNumber>20100</SequenceNumber>
      <Assembly>
        [YourDLLName], Version=1.0.0.0,
        culture=neutral, PublicKeyToken=[get it from the GAC]
      </Assembly>
      <Class>[YourDLLName].[YourEventReceiverClassName]</Class>
      <Data></Data>
      <Filter></Filter>
    </Receiver>
</Receivers>
</Elements>

This is a bit meatier. List Template ID refers to what kind of list it is. Generic list is 100, Document Library is 101, Issue List is something else. If in doubt, google "SharePoint List Template ID" and then decide which one fits. If you want to fire it off more than one type of list template, then you need to put in an event receiver for each one - that will be the <receivers></receivers> tags. Type is the event handler type, in this case ItemAdded. You will need a second one for ItemUpdated, and another one for ItemDeleted, should you have those also. Anything in square brackets is already defined when you compiled your assembly and you can get the public key token from the GAC. The sequence number just needs to be high enough to be unique.

So we have our feature files. Now it gets ugly and awkward. The only way we can install our feature (good readers feel free to correct me) is via an inelegant little tool called STSADM, which can be found, yes you guessed it, buried way way down in the Twelve Hive. You will generally find it somewhere similar to here:

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN\


So open up a Command Prompt window, cd to the above location or similar, and type the following piece of Shakespeare:


stsadm.exe -o installfeature -filename [YourFolderName]\Feature.xml


replacing YourFolderName and removing the square brackets of course. If all goes well, you're admin and king of the castle and the xml is well-formed, you should get a confirmation that the feature installed successfully. Once that done, you generally can forget about the feature unless there's an error in it. Most errors in features come from muggins like me copying files from elsewhere into the new folder and forgetting to change the assembly names, but never mind that, let's move on...


Navigate to the sharepoint site where you wish to deploy your new spiffy event handler. This does not have to be the top site, it can be several levels down. Go to Site Settings - Site Features. Your event handler will show up. Click Activate. Then go to your list, add /edit/delete your item and see if your field is updated. If not, never fear, you can debug it or instrument it. I always find the debugger a slippy process (oh for SP 2010 and its integrated environment!) so I will come back with some error handling code you can try.


But hopefully it works for you!

Tuesday 20 September 2011

Event Handlers - How I Do Them - Part 1 - Code

The first thing I would say about event handlers is that if you want to test them really effectively, get the code you want to execute and set it up in a Console Application. That way at least you can step through it and have a good goo before consigning it to an event handler, firing it up and shouting at the screen when Nothing Bloody Well Happens. You will at least have the advantage of knowing that if Nothing is Bloody Well Happening, it is not the fault of your beautiful code. It will be Something Else, as it so often is. Many a slip between cup and lip.

Here is what you need to make an event handler:
The code, compiled into a DLL, installed and snug in the GAC (that's the place where all the dlls go to heaven)
The Feature. This is a PITA to do the first time, but once you've done one, you've done 'em all.

Starting with the code. Open Visual Studio and select an ordinary bog standard C# class library project. Call it something meaningful, but please please not too long because you have to type it out later to install it in the GAC. (Yes, I know there is a post-build instruction script. Not much use when you're working on one machine and deploying on another!)

Now presumably at this stage you'll know what you want your event handler to actually do. I tend to name them accordingly: e.g. OnItemAddedCopyItem, or OnItemUpdatedUserField. Let's say we want to do something simple like populate a field with a particular value. And you want to do it when the list item is first added to the list.

First, go over to the far right where the project is sitting, right click on the References node and select Add References. Scroll right down to the very bottom and select Windows SharePoint Services. This affords you the run of the SharePoint API. Hurrah. Now go back to your class file and at the top, where all the declarations live, add:

using Microsoft.SharePoint;


and then when you get the line where it declares the class:


public class MyClass


change that to


public class MyClass : SPItemEventReceiver


so it knows where it's inheriting from. We are piggybacking on the SPItemEventReceiver class so we can do our own stuff with it. Once we've done that, then the Intellisense will start to kick in. The dropdown will autocomplete when we enter the following line within the class:


public override void ItemAdded(SPItemEventProperties properties)


(I am typing this from memory, btw, so apologies for any errors in transcription. The Intellisense will make up for my thick eejitry. It's so good it even adds in the base.ItemAdded(properties) line and the little curly brackets!)


The properties object is going to be very important as it's the carrier of all the information of what the user has just put into the list. It contains a siteID (which is useful) a ListItem property (which is even more useful) and other properties which are accessible via the ParentList property. For some reason I can only get the Web of the properties object via the ListItem property, but hell, I don't mind as long as I get it. So we do something of this or suchlike:


SPListItem item = properties.ListItem;
item["MyStatus"] = "Updated By Event Handler";
item.Update();


You need that last line to explicitly tell it to update the list, otherwise it won't. If you call Update and you have an ItemUpdated event on the same event handler, call the DisableEventFiring method on the this object:
this.DisableEventFiring()
or you will have event handlers firing all over the place and god knows what mayhem. There is also a property called


properties.Cancel


which when set to true is the equivalent of Harry Enfield yelling "OI! NO!" at the user when he or she tries to do something. If you do this, do try and have an error message telling them so or they will be utterly baffled!


Ok, once the dll has been compiled with a strong key, error handling sorted (mirthless laugh at this one) and you are happy, the next step is to deploy the dll in the GAC. Fire up the command prompt, change the directory to wherever your copy of gacutil.exe lives and type:


gacutil -i c:\myPath\EventHandlerThing.dll


(This is where I invariably learn I have forgotten the strong key and have to compile it all over again.)


Once the component is installed in the GAC, we will need to get in to c:\windows\assembly and right click our component to get the public key token to put into the feature. Or, as I like to call it silently, the Effing Feature. But that is to be continued in our next...

Very Quick Guide to the SharePoint Object Model

There are plenty of places to read about this in detail so I will just go through it quickly before getting into Event Handling. SharePoint 2007 deals with the following objects in toto:

Lists (of all kinds)
ListItems (items in the lists)
Fields (fields in the items in the lists in the bog down in the valley-oh. It usually suffices to indicate a field by putting the name in square brackets and quotes after the list item e.g. item["FieldName"] )
Document Libraries (which are really just lists. Everything in SharePoint is a list.)
Sites (except a Site is not a list. Forget what I just said.)

"Sites" is ambiguous because in 2007 They introduced the idea of Site Collections (and junked it in 2010 I think) so you have the site collection and then the actual site. I have only ever created a site collection once and that was using the Central Admin. And it was on a test machine far far away...but still they are important and I will explain why.

When you fire up Visual Studio and get ready to stick in your SharePoint code, bear in mind that the object
SPSite
means "site collection" and
SPWeb
means "site"

That means in practical terms in order to open up the oyster and get to the meat, you have to crack two shells before slurping the good bit (Apologies to any vegetarians reading this.) First you have to instantiate an SPSite object with your URL e.g. http://mysharepoint/sc/somesite/ and then you have to instantiate the web object using the Site object.

So, always using the using construct (so we never have to worry about calling the Dispose method) one instantiates the site as follows:

string url = " http://mysharepoint/sc/somesite/"
using (SPSite site = new SPSite(url))
{
using (SPWeb web = site.OpenWeb())
  {
   //now we want a specific list and we can do our work
    SPList list = web.Lists["myList"];
    SPListItem item = list.Items[0];
    Console.WriteLine(item.Title);
    Console.ReadLine();
  }
}

There is one exception to the using construct. This is when getting data from the current site. That's the one you have open in your browser.
SPContext.Current.Site
For obvious reasons, you do not want to Dispose() of your open site! Unless you're in bad odour with the Powers that Be and they unwisely made you site admin...

Stuff People Want Developers To Do with SharePoint

There are generally four things a user will want developers to do for them in SharePoint (When I say "SharePoint" I mean 2007. 2010 is not dissimilar but a bit more straightforward. When I have more programming experience in it I will say more about it!)

1. Event Handling. When modifying, creating or deleting a SharePoint item, sometimes it would be nice to do something a little extra above and beyond just completing the transaction. Event handlers are the bread and butter of SharePoint.

2. Web Parts. These are little forms (usually) that one can add in to a SharePoint page via the site galleries (found by Site Actions - Site Settings - Galleries - Web Parts.) When added in they can do various things with the SharePoint object model that are far more versatile and powerful than the built-in stuff.

3. Workflow This is not dissimilar to event handling, but is more visible and can be done out of the box (though the OOB implementation of workflow is extremely sparse.) My tool of choice for setting up workflows on tasks or list items is SharePoint Designer 2007 (which is a free tool), though this has disadvantages, the main one being that the .xoml files it builds (as an aside, where do Microsoft get these unpronounceable file extensions from?) are completely un-portable and can't be moved from one list to another. But once you bear that admittedly large limitation in mind, Designer can be handy enough.

It is also possible to code a workflow, but 2007 doesn't have as slick a dev environment as 2010 so I tend to stick to Designer.

Note part deux: if you do download Designer, ensure you already have SharePoint properly installed on the proper OS as previously detailed. I thought just having designer and SharePoint on a browser would be enough. Yes, you can laugh at my ignorance; if it saves you time then it makes me happy to be the butt of your derision!

4. Alerts While it is possible to set alerts on list items, one might often want some job to refresh or do some particular task once a day. One can (a) use an infinite looping workflow until the condition says stop, which is a bit clumsy to implement due to some flippin' Service Pack #90210 which stops infinite looping so you need TWO workflows or (b) create a timer job from the SharePoint Central Admin. I don't know how to do this yet (this will be a familiar, annoying refrain in the days and weeks to come) but when I do I will get back to you!

So in my next series of posts I will talk about 1, 2 and 3 and once I learn how to do 4, I'll talk about that too! With many pauses by the wayside to find some particular rant that interests me.

And yes, I know I mentioned the SharePoint Central Admin. Sounds like the Lubyanka, and probably every bit as cheering, but actually once you get in there, it's ok. It's basically another SharePoint site which decides the settings of all the machines on the SharePoint farm. Install that wherever you have the rest of your SharePoint installed and you should be a happy camper.

Setting Up a SharePoint Development Environment: What Not To Do

I feel like a bit of a fraud writing this as I have not yet managed to do it. I know it's only a matter of buying some server space from Amazon and setting up a virtual machine and and and AND...but I haven't got around to it yet. Here is why.

You cannot download SharePoint on anything other than Windows 2003 Server and Windows 2008 Server. You might possibly manage it on XP but given that is, in Microsoft time, back in ye stone age, who knows whether the memory in an XP machine might keel over. And no, there's no quick and easy way to get the object model on a machine that is not one of these two machines. So in order to set up you will need a virtual machine with one of the windows Server operating systems on it.

Seriously. Don't get your little Windows 7 netbook and think "I'm sorted now" when the thing starts downloading because it won't tell you for half a flippin' hour that, well actually, you can't get SharePoint on this OS, and no the little hotfix thingy won't work, and no you can't get some sort of lightweight object model and no, no, no. (Not that a sophisticated SharePoint girl like myself ever did anything like that!)

On Sharepoint 2010 you have streamlined integration between Visual Studio and the application, but since I work with Sharepoint 2007, I have to work with what I've got. And streamlined or not, you still need the right OS. Just warning yiz all. Don't pay the ferryman till he gets you to the other side.

First Post: What This Blog Presumes You Know

Greetings and welcome to all who alight here,

(or should that be a more Dante-esque style of greeting? As in "Abandon hope..."? We shall see.)

I am presuming that you've come here because you're relatively new to SharePoint development, like myself, and are interested in some of the tips, tricks, or various truths I've unearthed through my unscientific foray of the terrain. There are a couple of things I will presume you're familiar with, namely:

1. That .NET is not an item in which to catch .FISH;

2. That SharePoint (TM) does not refer to a borrowed ballpoint pen;

3. That C# is not just the key to a rather nice waltz by Chopin but also a rather clumsily named programming language;

4. That Object Orientation does not entail staring at and taking delight in various body parts of people one finds attractive. Well, it might, but not during the hours when one is customarily at work and may google hit this page, thank you very much!

I finally decided it was time to write this blog after a day spent fruitlessly trying to run a piece of code that would not work in spite of my obeying every rule in the book and even propitiating a fatted calf in its honour. Man, it stank out the office. But moving along swiftly - I want to put my pain down here to minimise yours - and welcome you around the fascinating ways of this piece of software. I am not pretending to be an expert in any way, shape or form. And my methods of programming will not appeal to everyone, especially people who are guilty of the sin of "galloping elegance" and want a purpose and a meaning for everything. But I hope maybe I can assist at least one other person and that would make me happy!