Skip to main content

Weblog Ton Stegeman [MVP]

Go Search
Home
  

ODC 2008
If you have a question or suggestion, please contact me through Windows Live Messenger.
My status: .

If I am not online, please send me an e-mail.
Weblog Ton Stegeman [MVP] > Posts > SharePoint 2007 Custom list schema and the Content Query Web Part
SharePoint 2007 Custom list schema and the Content Query Web Part

This post is a wrap up of previous blog posts that I have written on creating a custom list schema. I find myself searching for these posts every time I need to do this, so I thought it would be handy to have it all in one place (at least for myself). In one of our current projects, we were creating custom list schemas like this. we tried to query the lists (using Content Query Web Part) based on the custom content type that is associated with these list. At first this did not work and this post also shows how we fixed that. At the end of this article you will find the link to the ZIP file containing all code.

This article shows how to:

  • Create a custom site column from a feature
  • Create a custom content type using that site column from a feature
  • Create a custom list schema using that content type
  • Rename the Title field to a custom display name
  • Add a new view to the list, to show the new site column
  • Create a new feature that creates a new instance of the new list template
  • Populate the list with default items
  • Show items from lists based on this template using the Content Query Web Part

My solution has 2 features:

  1. site collection feature for registering site column, content type and list template.
  2. web feature for creating a list and populating it with data.

Step 1 – The Site Collection Feature

The XML snippet below is the XML from my feature.xml file that ends up in folder <SharePoint Root>\TEMPLATE\FEATURES\TST.CustomListTemplate. Nothing special to mention here, except that you can register multiple site columns, content types and list templates by creating 1 feature. You don’t need to create a new feature for every list template. SharePoint allows you to do that, but you end up with having a lot of features. If you want to have a feature for each list template, I would recommend creating hidden features and creating 1 wrapper features with activation dependencies.

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Feature Id="23ddef43-9d01-4127-9903-beeb77c28c5d"
   3:     Title="TST Custom List Template demo"
   4:     Description="Custom list template with a custom content type associated (Ton Stegeman)"
   5:     Version="1.0.0.0"
   6:     Scope="Site"
   7:     Hidden="FALSE"
   8:     xmlns="http://schemas.microsoft.com/sharepoint/">
   9:     <ElementManifests>
  10:       <ElementManifest Location="Fields.xml"/>
  11:       <ElementManifest Location="Contenttype.xml"/>
  12:       <ElementManifest Location="ListTemplates\Template.xml" />
  13:     </ElementManifests>
  14: </Feature>

The sample above activates three element manifests. These can be found in the next paragraphs.

Step 2 – The Site Column

In this XML snippet below, the new site column is created.

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   3:   <Field 
   4:     Type="Text" 
   5:     DisplayName="Demo Text Field"
   6:     Required="TRUE" 
   7:     MaxLength="255" 
   8:     Group="TST Demo Fields" 
   9:     ID="{ee69b92a-02cd-4a5d-b6bd-d1fec35c301f}" 
  10:     StaticName="DemoTextField" 
  11:     Name="DemoTextField"/>
  12: </Elements>

Most important thing to notice here is that I removed the spaces that are used in the displayname of the field from the internalname attributes of the field. I would recommend doing this for all special characters, otherwise you end up with encoded internal field names using ‘_x0020’. My custom site column would have had internal name ‘TST_x0020_Demo_x0020_Field’ if I did not remove the spaces from the internal names.

Step 3 – The Content Type

Below you can find the contents of Contenttype.xml. In the <FieldRefs> element you will find all fields that are used by the content type. You see a reference to our custom site column (it has the same ID and Name). The first field used in this sample is the title field that is renamed to have another displayname.

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   3:   <ContentType  ID="0x01008c43d032ae654d6e8b965c45acc4af3a"
   4:         Name="Demo Contenttype"
   5:         Description="Content type with a custom text field and a renamed Title field."
   6:         Group="TST Demo Fields" 
   7:         Version="0"
   8:         Sealed="FALSE"
   9:         ReadOnly="FALSE"
  10:         BaseType="0x01">
  11:     <FieldRefs>
  12:       <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" 
  13:                 DisplayName="Demo Title Field" 
  14:                 Name="Title" 
  15:                 Required="TRUE" 
  16:                 ShowInNewForm="TRUE" 
  17:                 ShowInEditForm="TRUE"/>
  18:       <FieldRef ID="{ee69b92a-02cd-4a5d-b6bd-d1fec35c301f}" 
  19:                 Name="DemoTextField" 
  20:                 Required="TRUE" 
  21:                 ShowInNewForm="TRUE" 
  22:                 ShowInEditForm="TRUE"/>
  23:     </FieldRefs>
  24:   </ContentType>
  25: </Elements>

There is something special about the ID of a custom content type. If you haven’t seen this before, you better read the something of the backgrounds, to understand this mechanism. Brett Maytom explains how it works. This MSDN article by Scot Hillier also explains it briefly.

Step 4 – The List Schema

Next thing to create is the list schema that is using our custom content type. I won’t publish the full content of the schema.xml file. You can find this in the attached ZIP file (see the end of this article). Important in the schema is that you will need to have the XML definition of the Field elements that are in your content type.

   1: <ContentTypes>
   2:   <ContentTypeRef ID="0x01008c43d032ae654d6e8b965c45acc4af3a">
   3:     <Folder TargetName="Item" />
   4:   </ContentTypeRef>
   5: </ContentTypes>
   6: <Fields>
   7:   <Field 
   8:     Name="LinkTitle" 
   9:     ID="{82642ec8-ef9b-478f-acf9-31f7d45fbc31}" 
  10:     DisplayName="Demo Title Field" 
  11:     Sealed="TRUE" 
  12:     SourceID="http://schemas.microsoft.com/sharepoint/v3" 
  13:     StaticName="LinkTitle">
  14:   </Field>
  15:   <Field 
  16:     Name="LinkTitleNoMenu" 
  17:     ID="{bc91a437-52e7-49e1-8c4e-4698904b2b6d}"
  18:     DisplayName="Demo Title Field"
  19:     Sealed="TRUE" 
  20:     SourceID="http://schemas.microsoft.com/sharepoint/v3" 
  21:     StaticName="LinkTitleNoMenu">
  22:   </Field>
  23:   <Field
  24:     ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" 
  25:     Type="Text" 
  26:     Name="Title" 
  27:     ShowInNewForm="TRUE" 
  28:     DisplayName="Demo Title Field" 
  29:     Sealed="TRUE" 
  30:     SourceID="http://schemas.microsoft.com/sharepoint/v3" 
  31:     StaticName="Title">
  32:   </Field>
  33:   <Field
  34:     Type="Text"
  35:     DisplayName="Demo Text Field"
  36:     Required="TRUE"
  37:     MaxLength="255"
  38:     Group="TST Demo Fields"
  39:     ID="{ee69b92a-02cd-4a5d-b6bd-d1fec35c301f}"
  40:     StaticName="DemoTextField"
  41:     Name="DemoTextField"/>
  42: </Fields>

The snippet above shows how to associate the custom content type we created in Step 3 with the list. The Fields elements contains all fields that we will use in our list (also our custom field that is part of the content type). If you don’t put the XML for your fields here, the content type will be associated with the list, but the fields will not be available in the list items. The snippet also shows how to rename the Title field to have a custom display name. It also renames the out of the box computed fields that use this Title field (like ‘Title (linked to item with edit menu)’).

Step 5 – The View

In the schema.xml file we also add the new view to our list. The snippet below just shows the <View> element.

   1: <View
   2:     BaseViewID="2"
   3:     Type="HTML"
   4:     WebPartZoneID="Main"
   5:     DisplayName="TST Custom View"
   6:     DefaultView="FALSE"
   7:     SetupPath="pages\viewpage.aspx"
   8:     ImageUrl="/_layouts/images/generic.png"
   9:     Url="DemoView.aspx">

Important attributes of this element are BaseViewID, SetupPath and Url. BaseViewID must be a unique ID for all views in your schema.xml file. SetupPath is a reference to a page that is used as template for your view page. If you don’t want to use custom form, set it to ‘pages\viewpage.aspx’. The Url will be the name of the aspx of the View itself. If you don’t specify the SetupPath, you will have to make sure your list schema folder has an aspx for the view page.

Step 6 – The List Template

After creating the list schema, next thing to do is to define the list template. This is the 3rd XML file that is referenced from our feature.xml file in step 1. The contents of my example is shown in the snippet below.

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   3:     <ListTemplate
   4:         Name="List"
   5:         Type="90010"
   6:         BaseType="0"
   7:         OnQuickLaunch="TRUE"
   8:         SecurityBits="11"
   9:         Sequence="410"
  10:         DisplayName="Demo list template with custom content type (Ton Stegeman)"
  11:         Description="Custom list with a custom content type associated, a renamed title field and an extra view."
  12:         Image="/_layouts/images/itgen.gif"/>
  13: </Elements>

Make sure that the Name attribute matches the name of the folder that contains the schema.xml file. In my case, the feature folder has a subfolder called ‘List’ that holds the schema.xml file. Second thing to look at is to define a unique number for your list template and use that in the Type attribute.

Step 7 – The Web Feature

In the previous steps we have completed our site collection feature that registers the schema for our custom list. In this step, we will create a Web scoped feature that creates a new instance based on this list schema and adds some items to it.

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Feature Id="05fd17d4-5f70-40ab-a5e9-975ba15df371"
   3:     Title="TST Custom List Instance demo"
   4:     Description="Custom list instance based on a custom schema (Ton Stegeman)"
   5:     Version="1.0.0.0"
   6:     Scope="Web"
   7:     Hidden="FALSE"
   8:     xmlns="http://schemas.microsoft.com/sharepoint/">
   9:   <ElementManifests>
  10:     <ElementManifest Location="Lists.xml" />
  11:   </ElementManifests>
  12:   <ActivationDependencies>
  13:     <ActivationDependency FeatureId="23ddef43-9d01-4127-9903-beeb77c28c5d"/>
  14:   </ActivationDependencies>
  15: </Feature>

The above snippet shows the XML file the feature. Make sure the Id for your feature is unique. Create a new GUID using http://www.newguid.com, use GuidGen in Visual Studio, or use the CodeRush/Refactor tools for SharePoint Developers by Andrew Connell.

The feature has a dependency on our site collection feature. SharePoint will first check if this feature is activated before activating the web feature.

Step 8 – The List

The feature in step 7 uses Lists.xml as element manifest. The contents of this file is shown in the snippet below. The FeatureId attribute is a reference to the feature that registered the list template. This is our site collection feature from Step 1, not the web feature from step 7!

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   3:   <ListInstance
   4:       FeatureId="23ddef43-9d01-4127-9903-beeb77c28c5d"
   5:       Title="Demo list"
   6:       Description="Demo list with custom schema and items."
   7:       Id="90010"
   8:       TemplateType="90010"
   9:       Url="Lists/Demolist">
  10:   </ListInstance>
  11: </Elements>

Make sure that the Id attribute matches the Type attribute of the ListTemplate element in Step 6. If you have spaces in the title of your list, it might be good to remove them from the url for your list. The Url attribute allows you to specify the url for your list. This will be the name of the rootfolder of the list.

Step 9 – The Data

In this step, we populate the list with some data once the list is created. Add the snippet below as a child element of the ListInstance element in the XML file of step 8.

   1: <Data>
   2:   <Rows>
   3:     <Row>
   4:       <Field Name="ID">1</Field>
   5:       <Field Name="Title">Title of the first item (created in the feature)</Field>
   6:       <Field Name="DemoTextField">Demo text first item</Field>
   7:     </Row>
   8:     <Row>
   9:       <Field Name="ID">2</Field>
  10:       <Field Name="Title">Title of the first item (created in the feature)</Field>
  11:       <Field Name="DemoTextField">Demo text first item</Field>
  12:     </Row>
  13:   </Rows>
  14: </Data>

For every Row, the ID field is set explicitly. It is not necessary to do this, but if you don’t do it and your feature gets re-activated, you end up with duplicated content in your list.

Step 9 – The Deployment

Deployment of our 2 features is done using a WSP package. In my Visual Studio solution, the folder TSTCustomListTemplate contains the site collection feature and all content. The folder TSTCustomListInstance contains all content for the web feature. The package is created using the DDF file below:

   1: ;
   2: .OPTION EXPLICIT     ; Generate errors 
   3: .Set CabinetNameTemplate=TST.TestList2007.ListTemplate.wsp
   4: .set DiskDirectoryTemplate=CDROM ; All cabinets go in a single directory
   5: .Set CompressionType=MSZIP;** All files are compressed in cabinet files
   6: .Set UniqueFiles="ON"
   7: .Set Cabinet=on
   8: .Set DiskDirectory1=Package
   9: manifest.xml manifest.xml
  10:  
  11: ; Include files for the Feature folder
  12: ..\TSTCustomListTemplate\Feature.xml TST.CustomListTemplate\Feature.xml
  13: ..\TSTCustomListTemplate\Contenttype.xml TST.CustomListTemplate\Contenttype.xml
  14: ..\TSTCustomListTemplate\Fields.xml TST.CustomListTemplate\Fields.xml
  15: ..\TSTCustomListTemplate\ListTemplates\Template.xml TST.CustomListTemplate\ListTemplates\Template.xml
  16: ..\TSTCustomListTemplate\List\schema.xml Features\TST.CustomListTemplate\List\schema.xml
  17:  
  18: ..\TSTCustomListInstance\Feature.xml TST.CustomListInstance\Feature.xml
  19: ..\TSTCustomListInstance\Lists.xml TST.CustomListInstance\Lists.xml
  20: ;

The manifest of the solution:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <Solution SolutionId="56e1580d-7eb8-4428-adea-1a17c120389c" xmlns="http://schemas.microsoft.com/sharepoint/">
   3:   <TemplateFiles>
   4:     <TemplateFile Location="Features\TST.CustomListTemplate\List\schema.xml"/>
   5:   </TemplateFiles>
   6:   <FeatureManifests>
   7:     <FeatureManifest Location ="TST.CustomListTemplate\Feature.xml"/>
   8:     <FeatureManifest Location ="TST.CustomListInstance\Feature.xml"/>
   9:   </FeatureManifests>
  10: </Solution>
  11:  

Deploying the solution is done using SharePoint Solution Installer from CodePlex. The snippet below shows the configuration for the installer, from setup.exe.config. The SolutionId needs to be the same value as the value of the SolutionId attribute in the manifest file.

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <appSettings>
   4:     <add key="BannerImage" value="Default"/>
   5:     <add key="LogoImage" value="None"/>
   6:     <add key="EULA" value=""/>
   7:     <add key="SolutionId" value="56e1580d-7eb8-4428-adea-1a17c120389c"/>
   8:     <add key="FarmFeatureId" value=""/>
   9:     <add key="SolutionFile" value="TST.TestList2007.ListTemplate.wsp"/>
  10:     <add key="SolutionTitle" value="TST Demo List Template"/>
  11:     <add key="SolutionVersion" value="1.0.0.0"/>
  12:     <add key="UpgradeDescription" value="Upgrades {SolutionTitle} on all frontend web servers in the SharePoint farm."/>
  13:     <add key="RequireDeploymentToCentralAdminWebApplication" value="false"/>
  14:     <add key="RequireDeploymentToAllContentWebApplications" value="false"/>    
  15:   </appSettings>
  16: </configuration>

Step 10 – The Test

After installing and deploying the solution, you can test the new list. First activate the site collection feature:

image

In the site(s) where you want to use your new list, activate the web feature:

image

If all is well, you now have a new list in your site called ‘Demo list’:

image

This list should have a custom view (TST Custom View), the Title field is renamed to ‘Demo Title Field’ and it has 2 items, as shown in the screenshot above.

Step 11 – Querying The Content

Last step in this article is to show how you can query for content in your new list(s) using the Content Query Web Part. Drag the web part onto a page and modify the properties. In the Query section you can select your new content type, as is shown in this screenshot:

image

The issue we have here is that you also need to specify the list type. In some cases, our custom list is not available in the dropdown. I was unable to figure out why the custom list template was not available in the dropdown, but I have seen it a few times. The way to solve this is to full configure your web part. Then save the changes. This will result in the message ‘This query has returned no items’. Export your web part and delete it from the page. Open the file in an editor. Find the property where that attribute Name has value ‘ServerTemplate’. Change the value of this property to the value of your Type attribute in the ListTemplate regristration in Step 6. In my case this is 90010.

   1: <webParts>
   2:   <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
   3:     <data>
   4:       <properties>
   5:         <property name="ServerTemplate" type="string">90010</property>
   6:       </properties>
   7:     </data>
   8:   </webPart>
   9: </webParts>

Save the .webpart file , import it onto your page and that’s it. Your web part will show the results from your custom lists:

image 

This ZIP file contains all the files I described in this article.
http://www.tonstegeman.com/Blog/Documents/TST.TestList2007.zip

Comments

Cool!

Very nice overview! thx for this!
at 1/4/2010 5:47 AM

Yes, but why?

Excellent post.
I'm left wondering why one would do this in code rather than through the UI.  My understanding is that if we apply custom columns and content types through the UI to the top-level site, then they are available throughout the site collection.

So why do it the hard way?  To be able to use it outside of the site collection or to be able to port it somewhere in the future?

I'm sure there is a great answer to this.  It's just not clear to me.

Thanks for indulging me.
E
at 3/26/2010 1:35 PM

User interface versus code

Eric,
Yes you are right, they are available throughout the site collection, and there is a good reason for doing it in code :-).
If you build a metadata model using site columns, content types and list schemas that is available in multiple site collections/web applications, this is the way to do it. In the user interface you would need to do it manually for every site collection. Another reason is that some companies wants deployments/changes like this to go through testing (technical test, acceptance test) before they are applied in production. It is the same way Microsoft uses to deploy all content types for example for task and contact lists in out of the box SharePoint.
Ton Stegeman at 3/27/2010 4:21 AM

Add Comment

Items on this list require content approval. Your submission will not appear in public views until approved by someone with proper rights. More information on content approval.

Title


Body *


Your city *


Type the name of the city you live in (making it easier to handle spam...)

CurrentDate *

Select the current date (see if this gives me fewer spam...)
Attachments

 Links

  SharePoint Object on CodePlex
  Screencast introducing SharePoint Objects
  Content by Type and Filter Web Parts on CodePlex
  Archive
  Archive (Calendar)

 My Latest Blog Posts

Scripting SharePoint 2007 setup: choices and conceptsUse SHIFT+ENTER to open the menu (new window).
Adventures in Visual Studio 2010: Migrate the Content By Type web part to SharePoint 2010Use SHIFT+ENTER to open the menu (new window).
Register SharePoint themes by using a featureUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010 development on Windows 2008 Server R2 – Getting StartedUse SHIFT+ENTER to open the menu (new window).
New release SharePoint Objects: features and groupsUse SHIFT+ENTER to open the menu (new window).
Constructing the url to the SharePoint Edit Permissions pageUse SHIFT+ENTER to open the menu (new window).
Screencast: introduction to SharePoint ObjectsUse SHIFT+ENTER to open the menu (new window).
SharePoint 2007 and Reporting ServicesUse SHIFT+ENTER to open the menu (new window).
SharePoint Objects – Insight in usage of your SharePoint artifactsUse SHIFT+ENTER to open the menu (new window).
SharePoint 2007 Custom list schema and the Content Query Web PartUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010 Silverlight Client Object Model – ExecuteQuery vs ExecuteQueryAsyncUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010, the Client Object Models and Bing MapsUse SHIFT+ENTER to open the menu (new window).
Having fun with SharePoint 2010, Silverlight 3 and Bing MapsUse SHIFT+ENTER to open the menu (new window).
Connecting TFS 2010 projects to SharePoint sitesUse SHIFT+ENTER to open the menu (new window).
Adding a database to the SharePoint database Server using SPDatabaseUse SHIFT+ENTER to open the menu (new window).