Monday, June 15, 2009

How to create List Templates and List Instances with VSeWSS 1.3

By Pako Simeonov

The task of every Sharepoint developer is to provide a solution that is easy to be deployed to the end systems of the client being it development, staging or production environment. In most of the cases a real life SharePoint project has a set of custom lists, document libraries and custom pages/web parts, so covering them will make the every day life of most of you a little bit easier. In this article I will cover the approach that I found most useful for myself from a developer stand point of view.

Once the VSeWSS 1.3 is being installed on the machine where Visual Studio .NET is located we get access to the Sharepoint project templates:

One of the available Sharepoint templates is the List Definition which is the one we can use to create a Sharepoint list. After selecting it we will have the chance to select what kind of template we want to create:

In our case we want to create a custom list, so we just have to select the Custom List base definition. However this is just a definition and what actually we want to do is to have a list instance based on that definition. We want to create the instance of the list automatically when we are deploying our application and not doing it manually. This can be done by just checking the Create an instance of this list option which will include one more file called instance.xml. This is how the list definition project looks like:

The files that we are interested in are the schema.xml, Instance.xml and ListDefinition.xml where the schema.xml is the most important one since it contains the definition of the list: fields, content types, views and forms and is the one that will be modified the most, while the other 2 files once created usually don't require any modifications. This is how the different files look like:
  1. ListDefinition.xml

  2. Instance.xml

  3. Schema.xml

Note: It is critical to have the Id attributes of the ListDefinition.xml and the Schema.xml and the FeatureId one in the Instance.xml synchronized all the time since they are used by Sharepoint to connect the different pieces together and if the mentioned identifiers are different usually this means troubles ones the feature is deployed to Sharepoint. Usually the result is either missing list instance or the list display form page shows you nothing but a standard browser message for missing page and you can not view the items in the list.

If we take a closer look at the Schema.xml we can see that actually our custom list has the Item content type as base one and that there are currently no fields defined, which is what we would expect for an empty list template. From this point on our task is to modify the list template in order to achieve our goal and we can do it the hard way or the easy way:
  1. Modify the schema.xml by hand. This includes creating the fields and content type definitions. I'm sure that there are people out there that are willing and are using this approach, but personally except for learning purposes I would never do that again since too many things can go wrong and it is just waste of time, although it is very helpful to know what is happening behind the scenes in order to troubleshoot the eventual problems that can occur and WILL occur later. This approach may work for a list or two but generally we have much more than that.
  2. Create the list using the SharePoint UI and export it using the SharePoint Solution Generator 2008 which comes with the VSeWSS 1.3. This approach allows us to create all we need using the SharePoint UI and later export it as Visual Studio .NET projects that have exactly the same structure as the one that was created using the Visual Studio .NET SharePoint template.
Without any doubt I would choose the second approach since it saves a lot of development time. Unfortunately it is not automating 100% of the work that needs to be done, but this is mostly related to the content types of the list. Even if we define a content type using the SharePoint UI that later is being used by the list in the result Visual Studio .NET project this content type is only included as embedded one in the schema.xml and when it is deployed to a different SharePoint there is no way to see it except from the list settings and thus there is no way to reuse it in different lists or use it as base of another content types. Of course since it is embedded in the schema.xml with the help of a console application that goes through the solution folder and searches for the schema.xml files and the Visual Studio .NET pre-build events we can easily automate the extraction of the content type in a separate xml that is being added to the project when we are re-building it and later being deployed as a separate content type feature automatically without the need to do anything extra, which is the ultimate goal of every developer. Of course probably there is a reason that this is currently not supported in the solution generator itself but writing such tool that covers 80% of the cases is not that difficult.

Here is what you need to do to export your lists/documents libraries using the solution generator:
  1. Open the SharePoint Solution Generator 2008 and select List Definition
  2. After you specify the url of the SharePoint site you will have the option to select a subset of all the site lists that exists on that site. In our case we just need the MyCustomList one.
  3. The last step of the process is to specify the name of the project and the location where the solution generator should create the Visual Studio .NET project.
Usually as in every real life project the lists will need to be modified - add or remove a field, etc. Once we have the project with the lists created this is an easy task. The modification can be done using the SharePoint UI and exported the same way. In most of the cases I usually modify the ListDefinition.xml and the Instance.xml files in order to change the titles and the descriptions of the lists and I don't want these files to be overwritten, so I generate the new definition in a temporary folder and just copy the Schema.xml in the real project, since it is the one that contains the list modifications. As I mentioned earlier you need to be careful here since during the new export the Id attribute of the List element in the Schema.xml file is being changed, so you need to propagate it to the Instance.xml and the ListDefinition.xml files.

Once we have the Visual Studio .NET project ready we can deploy the lists to different sites quite easily:

Note: Deploying to a site that still doesn't have existing list instances of the list templates is straightforward and will hardly ever cause a problem. However this is not the case if we just modified the list templates and are trying to re-deploy it to a site where it was deployed previously and we have already existing list instances created using the list templates. The chance that you will get the following message is pretty big: Feature {Guid} for list template '100' is not installed in this farm. The operation could not be completed. I've found this problem the painful way and after some analyses actually the reason for the error is pretty obvious. The resolution of this problem will be covered in Fixing 'Feature {Guid} for list template is not installed in this farm. The operation could not be completed' , where I will explain the exact reason and will show I handy tool I've developed that can automatically fix the problem for you.

Bookmark and Share


oidatsmyleg said...

An alternative approach to what you've described is to create the Content Type first. Then use the option of creating a List Definition based upon a Content Type (using VSeWSS) and this will correctly reference the Content Type and ensure its reuse across a number of lists.

This is the only means I know of by which it is possible to define lookup columns in a List Definition that use data from a different Content Type.

Pako Simeonov said...

This was the approach I was initially using. I used to create the content types using the UI and after that exporting them with an stsadm command (custom one). Then I followed the approach that you mentioned and it worked good for a while, but had some inconvenient things like:

1. If I go to another SharePoint server I had to add the custom stsadm command.
2. The command was able to export content types from a group and was placing them in one big xml file, so I had to put them in separate files.
3. The export format was not quit compatible because of some attributes - Version, Customization, etc. which was causing errors when I try to redeploy.

I really started suffering when I added some custom views to the list or columns that are not part of a content type. Also in my case I had one custom attribute in the Field element where I was storing column level permissions, which had to be in the list definition Field element and not in the content type one. As a result I had to update the list schema all the time, additionally to the content type itself. So I asked myself the question - why should I update the content type AND the list schema all the time when I can save 1/2 of the work and just update the schema an automate the rest with minimal effort - you know that we the developers sometimes are little lazy :)

As a result of my struggling I found that the procedure I described in my article works best for me and requires the least effort and can handle "special" cases that in the real life project and not that rare unfortunately.

Yeah sure I could of done one batch file that will run the stsadm command, then process the result, etc, but why making the things more complex when simply processing a little bit the schema.xml does even better job and you just rely on one single file.