>
Blog
Book
Portfolio
Search

7/10/2008

22525 Views // 0 Comments // Not Rated

Programmatically Creating And Programmatically Deleting List Templates Programmatically Via A SharePoint 2007 Programmatic Feature That Uses Code And Not XML Programmatically!

(Sorry that title is so obnoxious. I really want people to find this post, as it answers questions about programmatic access to SharePoint List Templates that I couldn't find elsewhere! And I want to stop as many people as I can from writing needless and onerous several thousand line XML files...)

Somehow, the need to provision a SharePoint list via a Feature hasn't made it's way to my task list until now. Of course, I know the right way to do it: create dozens of XML files that contain billions of lines of markup each, and install them through stsadm, probably via a nice batch file that copies everything to where it needs to live.

Ugh.

So I started playing with feature XML files and element XML files and schema XML files and all of that. It is pretty cool how you can declaratively create small pieces of SharePoint and granularly install them to customize almost anything in your portal - workflows, menus, event handlers, etc.

But unfortunately, there are so many gotchas with this process, so many subtle inconsistencies among the schemas, and basically so many ways to screw it up that I almost considered bailing on the whole Feature thing and hacking together a different method to provision a list template. I mean, seriously, who is going to go though literally six thousand lines of markup to figure out how to customize a simple list that would otherwise take five minutes to do via the UI?

(Okay, so it's less than a billion, but still: the amount of XML in the schema.xml file needed for a list template is waaay too daunting to deal with...)

I am a PROGRAM-mer, not an XML-mer. I want to write code, not markup. So, after getting scared off from declaratively creating list templates, I turned my eyes, as I usually end up doing, toward the SharePoint API and procedural logic and looked there for the answer. But instead of helping me out, it instead punched me in the face.

The API for template objects is extremely thin. The SPListTemplate object is a joke: it has maybe only a dozen or so properties, almost all of which are read only. I, having worked with SharePoint for over three years now, assumed that from this object, I could programmatically manipulate a template, and update it the same as I know I can for an instance of a template.

But nooooooooooooooooo-ooooooooooooooooooooooooooo.

There's no collection for the views, fields, or forms! There's no way to set anything meaningful! It's as though Microsoft really wants us to plunge into that tempestuous schema.xml file and drown to death in a cesspool of angle brackets. (See existing examples of these and other XML files in SharePoint's FEATURES folder, which is by default C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\FEATURES.)

So the template API was out. There had to be another way. Enter my second brain: Google. Unfortunately, I searched and searched, but nothing helpful came back. Either no one has ever tried to create a template programmatically, no one could figure it out, or what I'm about to discuss is so trivial and obvious that it wasn't even worth mentioning.

Well, all I can say is that I hope it's not the later...

The approach I went with was to create a list on the fly, customize it from scratch all though code, save it as a template, then thank it for all its hard work and delete it. Since the instance (SPList) of a template (SPListTemplate) gives us programmatic access to every nook and cranny of a list object, why screw around with massive XML files that need thousands of lines of markup to accomplish what we can do in dozens of lines of code?

So all I did was create a feature with no elements that has a Feature Receiver. Here's what the XML looks like:

Code Listing 1

  1. <Feature xmlns="http://schemas.microsoft.com/sharepoint/" Id="35DFB63F-1675-4e23-B7C2-0E941A609EA5" Title="Test Document Library" Description="This Feature is amazing." Version="0" Hidden="FALSE" ReceiverClass="assembly name.class name" ReceiverAssembly="assembly FQN" Scope="Web">
  2. <ElementManifests />
  3. </Feature>

Then, in the activated event handler, I do the following (this is just example code to show some of the things you do can):

Code Listing 2

  1. public override void FeatureActivated(SPFeatureReceiverProperties properties)
  2. {
  3. //open web
  4. using (SPWeb web = properties.Feature.Parent as SPWeb)
  5. {
  6. //create list
  7. web.AllowUnsafeUpdates = true;
  8. SPList list = web.Lists[web.Lists.Add("title", "desc", SPListTemplateType.DocumentLibrary)];
  9. //do stuff on list
  10. list.EventReceivers.Add(SPEventReceiverType.ItemAdded, "assembly FQN", "assembly.class name");
  11. list.EnableAttachments = true;
  12. list.Update();
  13. //manipulate views
  14. for (int n = list.Views.Count - 1; n >= 0; n--)
  15. {
  16. //remove all views except the default
  17. SPView view = list.Views ;
  18. if (!view.DefaultView)
  19. list.Views.Delete(view.ID);
  20. }
  21. //add fields
  22. SPField field = list.Fields[list.Fields.Add("field", SPFieldType.URL, false)];
  23. field.ShowInDisplayForm = true;
  24. field.ShowInEditForm = false;
  25. field.ShowInListSettings = true;
  26. field.ShowInNewForm = false;
  27. field.ShowInVersionHistory = true;
  28. field.ShowInViewForms = true;
  29. field.Sortable = true;
  30. field.Update();
  31. //customize views
  32. SPView view = list.DefaultView;
  33. view.Query = "<OrderBy><FieldRef Name='Created By' Ascending='False' /></OrderBy>";
  34. view.ViewFields.DeleteAll();
  35. view.ViewFields.Add(list.Fields["Created By"]);
  36. view.ViewFields.Add(field);
  37. view.Update();
  38. //save as template and delete
  39. list.SaveAsTemplate("test.stp", "test template", "test", false);
  40. list.Delete();
  41. web.Update();
  42. }
  43. }

As you can see, I only used enough XML in this Feature definition to allow my code to run.

And like all good software, we want to make sure we clean up after ourselves when it's time to be deactivated. This is especially true for SharePoint features, since they are very granular and can be deactivated and reactivated over and over again in different places around your portal. Make sure that whenever you create a Feature, you have code in your FeatureDeactivated event handler to erase the Feature's footprint.

But how the hell do you delete a list template? I thought the API was slim in terms of creating and accessing templates; there's NOTHING about deleting them. You can't even do it though stsadm. (And believe me, I thought long and hard about throwing a Diagnostics.StartProcess call in my SharePoint code...ugly...)

The only place I could see where you can delete list templates AT ALL is via the UI in the List Template Gallery. Hmm. Then it hit me: list templates are nothing more than items in a list! If we delete the item in the list, do we effectively "uninstall" the template? You bet your sweet face we do!

Here's the code:

Code Listing 3

  1. public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
  2. {
  3. //open web
  4. using (SPWeb web = properties.Feature.Parent as SPWeb)
  5. {
  6. //open the list template gallery for this web
  7. SPList gallery = web.Lists["List Template Gallery"];
  8. foreach (SPListItem template in gallery.Items)
  9. {
  10. //find the timestamp doc lib
  11. if (template.Title.Equals("test template"))
  12. {
  13. //delete this template
  14. template.Delete();
  15. gallery.Update();
  16. break;
  17. }
  18. }
  19. }
  20. }

Very straight forward. Not only does this remove the template from the gallery (I was afraid I'd only get that far and have orphaned templates in my portal), but it removed them from the template selection screens as well! Installing a new template afterwards with the same works, (which would otherwise have blown up; name conflicts in SharePoint usually throw an exception) so you know it's legit!

And the best part is that existing lists based on the template STILL WORK after deletion! I've seen lists that were based on templates that were later deleted start throwing hardcore errors, and not even allowing the user to get to a screen where they could delete the now "dead" list. With my technique, it's clean, easy, legit, and doesn't break anything!

Have Fun!

No Tags

No Files

No Thoughts

Your Thoughts?

You need to login with Twitter to share a Thought on this post.


Loading...