More Soma Please…

Developers are in a race with the Universe to create better idiot-proof programs, while the Universe is trying to create better idiots. So far the Universe is winning.

Creating a SharePoint List Template using a Feature

Posted by Phil Harding on September 23, 2009

Building on the last post about creating a content type, in this post we’ll create a custom list template which uses that content type and deploy it using a feature.

If you’ve ever taken a look at the schema definition file for a list template, you may baulk at it’s size and complexity, in fact you can ignore most of this, creating a custom list template turns out to be relatively straightforward.

A list template comprises 2 files;

  1. The list template.xml file
  2. The list template schema.xml file

Create the list template.xml file and add it to your feature manifest file;

<?xml version="1.0" encoding="utf-8"?>
<Elements Id="8f1d5d09-6834-40b7-b245-c62c0811596e" xmlns="http://schemas.microsoft.com/sharepoint/">
	<ListTemplate Name="MyTestListTemplate"
		  DisplayName="TestList"
		  Description=""
		  BaseType="0"
		  Type="100"
		  OnQuickLaunch="TRUE"
		  SecurityBits="11"
		  Sequence="410"
		  Image="/_layouts/images/itgen.gif" />
</Elements>

Note the Name and BaseType attributes, we will create a subfolder with the same name as the Name attribute and place the schema.xml file there. The BaseType attribute indicates the list type from which this list templates inherits which in this case is the basic custom list.
Add the listtemplate.xml file to the feature manifest;

<?xml version="1.0" encoding="utf-8"?>
<Feature  Id="adc873b4-fe40-4169-8304-c9a1dd697c2b"
          Title="MySiteTypes"
          Description="Description for MySiteTypes"
          Version="12.0.0.0"
          Hidden="FALSE"
          Scope="Site"
          ActivateOnDefault="FALSE"
          ImageUrl="GenericFeature.gif"
          DefaultResourceFile="core"
          xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest Location="sitecolumns.xml"/>
    <ElementManifest Location="mycontenttype.xml"/>
    <ElementManifest Location="listtemplate.xml"/>
  </ElementManifests>
</Feature>

Now for the schema.xml file, create a subfolder in your feature named “MyTestListTemplate” and in that folder create a new XML file called schema.xml.
lt01
To populate this file you have some options;

  1. Create it manually (take a look at the huge amount of CAML!!)
  2. Copy the base Custom List schema.xml file and modify it
  3. Craft the list/views etc in SharePoint using the UI and export the template using SharePoint Manager or the SharePoint Solution Generator

Using one of these methods you should then modify your schema.xml file as follows;

Change the Name, Title and URL attributes;

<List Name="TestList" Title="TestList" Description="" Direction="0" BaseType="0"
		Url="Lists/TestList"
		FolderCreation="FALSE"
		EnableContentTypes="TRUE"
		Type="100"
		Id="8f1d5d09-6834-40b7-b245-c62c0811596e"
		xmlns="http://schemas.microsoft.com/sharepoint/">

The contenttypes section should contain only the content type(s) you’ve defined for this list;

	<ContentTypes>
		<ContentTypeRef ID="0x0100928100FAB05A4148BD6F4C8E6A716B40" />
	</ContentTypes>

To the Fields section add only the field definitions defined in the content types list in the above contenttypes section;

<Fields>
	<Field Type="Number" DisplayName="MyReference" Required="FALSE" Decimals="0" Commas="FALSE" Group="My Group" ID="{918BA6B8-60BC-417e-AEE5-F9E1A0A59144}" StaticName="MyReference" Name="MyReference" Customization="" SourceID="{6a05fd42-eda7-4fb7-9bfd-2a35ded20c24}" ColName="float1" RowOrdinal="0">
		<Default>0</Default>
	</Field>
	<Field Type="Choice" DisplayName="MyType" Description="Description of MyType" Required="FALSE" Format="RadioButtons" FillInChoice="FALSE" Group="My Group" ID="{1FDC83BE-D596-45e2-9DD3-AD3338798784}" StaticName="MyType" Name="MyType" Customization="" SourceID="{6a05fd42-eda7-4fb7-9bfd-2a35ded20c24}" ColName="nvarchar3" RowOrdinal="0">
		<Default>Person</Default>
		<CHOICES>
			<CHOICE>Person</CHOICE>
			<CHOICE>Place</CHOICE>
			<CHOICE>Thing</CHOICE>
		</CHOICES>
	</Field>
	<Field Type="Note" DisplayName="MyDetails" Description="Description of MyDetails" Required="FALSE" NumLines="5" RichText="FALSE" Sortable="FALSE" Group="My Group" ID="{AB31BEFA-A178-4a1e-B324-B73E9C15D5A8}" StaticName="MyDetails" Name="MyDetails" AllowHyperlink="TRUE" RichTextMode="Compatible" IsolateStyles="FALSE" AppendOnly="FALSE" Customization="" SourceID="{6a05fd42-eda7-4fb7-9bfd-2a35ded20c24}" ColName="ntext2" RowOrdinal="0">
		<Default>Unknown</Default>
	</Field>
</Fields>

Modify the ViewFields section of each View (there are 2 views defined by default, a HTML view and the AllItems view) to include the columns required;

	<ViewFields>
		<FieldRef Name="Attachments" />
		<FieldRef Name="LinkTitle" />
		<FieldRef Name="MyReference" />
		<FieldRef Name="MyType" />
		<FieldRef Name="MyDetails" />
	</ViewFields>

You might also want to use the LinkTitleNoMenu column which is a straight link column to the proprty page of the list item, rather than the LinkTitle column which includes the dropdown ECB menu.

If you have exported the schema.xml file using one of the methods mentioned, you should check that your View element(s) (except the view with BaseViewID of 0 zero) have the SetupPath attribute set correctly.

<View DefaultView="TRUE"
      Type="HTML" DisplayName="All Items" Url="AllItems.aspx" Level="1" BaseViewID="1" ContentTypeID="0x"
      SetupPath="pages\viewpage.aspx"
      ImageUrl="/_layouts/images/DECISION.GIF" WebPartZoneID="Main">
   ...
   ...
</View>

Finally check that the Forms section is as follows;

    <Forms>
      <Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
      <Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
      <Form Type="NewForm" Url="NewForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
    </Forms>

Build and Deploy the feature to your SharePoint site and activate the MySiteTypes feature.
lt02
The new “Test List” list template will now appear in the Custom Lists section of a sites Create page as shown.
lt03
You can now create a sample list based on this template.
lt04

Posted in Sharepoint, Software Development | Tagged: , , | Leave a Comment »

Creating a SharePoint Content Type using a Feature

Posted by Phil Harding on September 8, 2009

Following on from this post in which I created Site Columns using a feature, in this post we will create a Content Type based on these site columns.

For an introduction to Content Types see the MSDN site.

Building on the solution in the previous post, we will create a Content Type called “My Site Content Type” using the site columns created there.

Add a new XML file called mysitecontenttype.xml to the MySiteTypes folder as shown below.

sct01

Open the new XML file, delete it’s default content and drop in the following Content Type XML.

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
	<ContentType ID="0x0100928100FAB05A4148BD6F4C8E6A716B40"
					 Name="My Site Content Type"
					 Description="My Site Content Type"
					 Group="My Group">
		<FieldRefs>
			<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
			<FieldRef ID="{918BA6B8-60BC-417e-AEE5-F9E1A0A59144}" Name="MyReference" Required="TRUE" />
			<FieldRef ID="{1FDC83BE-D596-45e2-9DD3-AD3338798784}" Name="MyType" Required="TRUE" />
			<FieldRef ID="{AB31BEFA-A178-4a1e-B324-B73E9C15D5A8}" Name="MyDetails" Required="TRUE" />
		</FieldRefs>
		<XmlDocuments>
			<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
				<FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
					<Display>ListForm</Display>
					<Edit>ListForm</Edit>
					<New>ListForm</New>
				</FormTemplates>
			</XmlDocument>
		</XmlDocuments>
	</ContentType>
</Elements>

You can create this XML in a few ways;

  1. Create the Content Type using the SharePoint UI and export it, using for example Andrew Connells STSADM commands.
  2. Extract the Content Type XML using SharePoint Manager.
  3. Create it manually.

If you use the first approach, ensure that the XmlDocument element has the NamespaceURI attribute shown, if you don’t, you may experience the same problem I had when I failed to do this, discussed here.

If you use SharePoint Manager, you will need to change the Field elements to FieldRef elements.

In either case you must ensure that the ID attributes have unique values. If you exported the content type from SharePoint to package it into a feature then you should certainly change the the content type ID attribute value.

Having created the Content Type, we now need to modify the Feature manifest file, feature.xml, to tell it about the Content Type. Open the feature.xml file and add an ElementManifest element, as shown below.

<?xml version="1.0" encoding="utf-8"?>
<Feature  Id="adc873b4-fe40-4169-8304-c9a1dd697c2b"
          Title="MySiteTypes"
          Description="Description for MySiteTypes"
          Version="12.0.0.0"
          Hidden="FALSE"
          Scope="Site"
			 ActivateOnDefault="FALSE"
			 ImageUrl="GenericFeature.gif"
          DefaultResourceFile="core"
          xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest Location="sitecolumns.xml"/>
    <ElementManifest Location="mycontenttype.xml"/>
  </ElementManifests>
</Feature>

Use the WSPBuilder Build and Deploy commands to build and deploy to SharePoint.

After the solution has been deployed, browse to your SharePoint Site Collection and go to Site Settings – Site Collection Features and activate the MySiteTypes feature.

sc05

Navigate to Site Settings – Site Content Types under the Galleries section and your new Content Type is ready for use.

sct02

So lets try it out.

  1. Create a new Custom List
  2. Navigate to the List Settings and choose Advanced Settings under General Settings.
  3. On the Content Types section, choose Yes for Allow Management of Content Types.
  4. Click OK.

sct03

  1. Now choose Add from existing content types

sct04

  1. Click Add
  2. Click OK

You’ll notice that the List Settings now includes the Content Types site columns in its Columns section.

sct05

Modify the default view to include those columns. Now when you add a new list item you will see the option to add a new “My Site Content Type”.

sct06

The form used to edit the Content Types columns is generated by SharePoint at runtime, but you can customize these or provide your own.

sct07

Posted in Sharepoint, Software Development | Tagged: , | 2 Comments »

Query XML with Namespaces using XPathNavigator

Posted by Phil Harding on September 2, 2009

Querying XML data which has namespace qualifications can be a little confusing at times, consider the following document;

<query xmlns="http://platinumdogs.com/schema/reporting/query">
 <applicationid value=" CA6E1CAE-A74F-4542-9391-82DF90301B28" />
 <prf:mdschemaid value=" 25c36533-6e4b-4034-921c-bcc7a18e1ac5" xmlns:prf="http://platinumdogs.com/schema/metadata/query" />
 <mdschemaversion value=" 1" />
 <pagesize value=" 10" />
 <numberofpagesrequired value=" 1" />
 <pageindex value=" 1" />
 <startrow value=" 1" />
 <enablepagedaccess value=" True" />
 <ydimension value=" AGE_BAND_10_YEAR" />
 <xdimension value=" AGE_BAND_05_YEAR" />
 <xdimension_limit value=" 0" />
 <fact_measure value=" NUMBER_OF_DEATHS" />
</query>

This example is declaring a default namespace qualification at the root of the document, which scopes the inner part of the document to the default namespace, which in this case is http://platinumdogs.com/schema/reporting/query.

Given this example you might expect this code to return the <applicationid> node.

	var xnQuery = new XPathDocument(new XmlTextReader(new StringReader(myXmlString))).CreateNavigator();
	var xnExpressions = xnQuery.Select("/query/applicationid");
	Console.WriteLine("Node Count = " + xnExpressions.Count.ToString());

But it doesn’t return anything at all because we haven’t told the the XPathNavigator about the namespace used in the XML document. To do that we need to create an XmlNamespaceManager object, add the namespace declaration to it and provide this to the XPathNavigator.Select method, as shown below.

	var xnQuery = new XPathDocument(new XmlTextReader(new StringReader(myXmlString))).CreateNavigator();

	var ns = new XmlNamespaceManager(xnQuery.NameTable);
	ns.AddNamespace("ns0", "http://platinumdogs.com/schema/reporting/query");

	var xnExpressions = xnQuery.Select("/ns0:query/ns0:applicationid", ns);
	Console.WriteLine("Node Count = " + xnExpressions.Count.ToString());

Notice that I’m using the namespace prefix "ns0" in my XPath query, while the XML document declares the default namespace. This doesn’t matter because the important part is the Namespace URI (“http://platinumdogs.com/schema/reporting/query”) itself, the prefix part (whether explicit or the default) is effectivly a key or alias.

If the document had been declared using an explicit namespace declaration, say xmlns:ns="http://platinumdogs.com/schema/reporting/query", I could still use the "ns0" prefix in my XPath query.

So the Namespace prefix doesn’t matter, but doesn’t the XPathDocument know all about the NamespaceURI’s declared in the document? It does, but you still need to provide the list of NamespaceURI’s to the XPath query processor because it’s of relevance to the XPath query you specify in your call to the XPathNavigator.Select method. 

You can create an XmlNamespaceManager object and populate it using the Namespace declarations found in a XPathDocument. This is usefull when you don’t know what NamespaceURI’s you’re going to come across, the following code snippet shows how to do this.

	var xnQuery = new XPathDocument(new XmlTextReader(new StringReader(myXmlString))).CreateNavigator();

	var ns = new XmlNamespaceManager(xnQuery.NameTable);
	var nodes = xnQuery.Select("//*");

	while (nodes.MoveNext())
	{
		var nsis = nodes.Current.GetNamespacesInScope(XmlNamespaceScope.Local);
		foreach (var nsi in nsis)
		{
			var prf = nsi.Key == string.Empty ? "global" : nsi.Key;
			ns.AddNamespace(prf, nsi.Value);
			Console.WriteLine("Adding... prefix=" + prf + ", uri="+nsi.Value);
		}
	}

	var xnExpressions = xnQuery.Select("/global:query/global:applicationid", ns);
	Console.WriteLine("Node Count = " + xnExpressions.Count);

	xnExpressions = xnQuery.Select("/global:query/prf:mdschemaid", ns);
	Console.WriteLine("Node Count = " + xnExpressions.Count);

The code queries the document for all nodes, iterates over those nodes, gets the Namespaces in Scope and adds the declaration to the XmlNamespaceManager.  Notice how the default namespace is handled by substituting "global" for the default namespace prefix which is an empty string.

Posted in Software Development | Tagged: , , | Leave a Comment »

SharePoint Integer and Numeric Field (Site Column) Types

Posted by Phil Harding on September 2, 2009

One of the Field types available for defining Site Columns is Integer, another is Numeric. When you create a site column using the UI, you won’t see an option for creating a site column of type Integer.

However if you create a site column of type Integer and provision it into SharePoint using a feature, you won’t see it in the Site Columns gallery. Curious, if you then build a Content-Type using this Site Column and provision that into SharePoint, and say, create a list using this Content-Type, voila, at last you will see your “Integer” type Site Column.

It turns out that the base Integer field type is marked UserCreatable = FALSE in the fldtypes.xml file, which mean that you can’t create fields of this type using the UI, but they can still be included as part of a Content Type.

Ok so why might you want to create a field (Site Column) of type Integer. Lets say you want to create a Site Column to store 4 digit years.

e.g.      2009

Well the Numeric field type allows you to set the Decimals part to zero, so the UI won’t display 2009.0. Great, except that the UI will display the year value 2009 as 2,009 with embedded commas,  which as a year display format isn’t great. The Field element also has an attribute called COMMAS which is supposed to control whether to display values using commas.

But, this attribute seems to be ignored, even if you set this attribute to FALSE the value is still displayed using commas.

What to do? enter the Integer field type, it doesn’t suffer from this problem and as long as you only want to use it as part of a Content-Type it’ll work perfectly.

Posted in Software Development | Tagged: , | Leave a Comment »

Creating SharePoint Site Columns using a Feature

Posted by Phil Harding on September 1, 2009

In this post we will be creating some Site Columns and provisioning them into SharePoint using a feature. The tools we will be using are Visual Studio 2008 and WSPBuilder.

First create a new Visual Studio project using the WSPBuilder Empty project template.

sc00

Your new solution should look something like this.

sc01

Add a new project item to the solution using the WSPBuilder Blank Feature template.

sc01a

Name your feature something sensible and open the feature.xml feature definition file.

<?xml version="1.0" encoding="utf-8"?>
<Feature  Id="adc873b4-fe40-4169-8304-c9a1dd697c2b"
          Title="MySiteTypes"
          Description="Description for MySiteTypes"
          Version="12.0.0.0"
          Hidden="FALSE"
          Scope="Site"
          ActivateOnDefault="FALSE"
          ImageUrl="GenericFeature.gif"
          DefaultResourceFile="core"
          xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest Location="sitecolumns.xml"/>
  </ElementManifests>
</Feature>

Update the feature.xml as above. The feature.xml generated by WSPBuilder is generally all you need, the changes I’ve made are to the Description attribute and the Scope attribute. Site Columns must be scoped at the Site Collection level.

I’ve added 2 other attributes;

  1. ActivateOnDefault – Enable/Disable activating this feature when the feature is installed.
  2. ImageURL – an image URL (relative to …/_layouts/images) to display against the feature in the feature gallery.

Now rename the features single element manifest file from elements.xml to sitecolumns.xml, open sitecolumns.xml and add the following XML.

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
	<Field Type="Number" DisplayName="MyReference" Required="FALSE" Decimals="0" Commas="FALSE"
			 Group="My Group" ID="{918BA6B8-60BC-417e-AEE5-F9E1A0A59144}"
			 StaticName="MyReference" Name="MyReference">
		<Default>0</Default>
	</Field>
	<Field Type="Choice" DisplayName="MyType" Description="Description of MyType"
			 Required="TRUE" Format="RadioButtons" FillInChoice="FALSE" Group="My Group"
			 ID="{1FDC83BE-D596-45e2-9DD3-AD3338798784}" StaticName="MyType" Name="MyType">
		<Default>Person</Default>
		<CHOICES>
			<CHOICE>Person</CHOICE>
			<CHOICE>Place</CHOICE>
			<CHOICE>Thing</CHOICE>
		</CHOICES>
	</Field>
	<Field Type="Note" DisplayName="MyDetails" Description="Description of MyDetails"
			 Required="FALSE" NumLines="5" RichText="FALSE" Sortable="FALSE" Group="My Group"
			 ID="{AB31BEFA-A178-4a1e-B324-B73E9C15D5A8}" StaticName="MyDetails" Name="MyDetails"
			 AllowHyperlink="TRUE" RichTextMode="Compatible" IsolateStyles="FALSE" AppendOnly="FALSE">
		<Default>Unknown</Default>
	</Field>
</Elements>

I’ve added 3 site solumns of type Number, Choice and Note (multi-line text field). Each field definition must have a unique ID value which is a GUID, and for clarity in this example I’ve grouped these fields together using the Group attribute.

There is quite a lot of CAML there even just for 3 fields, you can craft this manually yourself, but there is an easier way. You create the site columns using the UI, when you’re happy with them you use a third party tool to export the site column definitions concerned then take the output XML and place it in your feature. There’s a couple of tools I use, one is a custom STSADM command by Andrew Connell, the other is the SharePoint Manager which is an explorer like tool which allows you to drill down to field definitions to view the XML, it’s then just a copy and paste into your feature.

When exporting site column definitions this way, you must create new GUID ID values for each field.

More information about the Field definition schema can be found here and here on MSDN.

When naming fields I would usually set the Name and StaticName attributes to values without any whitespace and use the DisplayName attribute to give the field a more informative display name.

Now use the WSPBuilder Build and Deploy commands to build and deploy the Solution to SharePoint.

sc02a

After the solution has been deployed, browse to your SharePoint Site Collection and go to Site Settings – Site Collection Features and activate the MySiteTypes feature.

sc05

Then navigate to Site Settings – Site Columns under the Galleries section, and you should see your new site columns listed under the custom group “My Group”.

sc06

You can now use these site columns when you create Lists or Document Libraries, from the List or Document Library settings page choose Add from existing Site Column.

sc02b

Choose the columns to add to the list (check “Add to default View”), click OK and your custom site columns will be added to the List or Document Library.

sc07

Site Columns on their own however, aren’t entirely useful, it’s more usual to define sets of Site Columns, and using them to form Content Types, which in SharePoint are a whole lot more useful and represent a functional leverage point.

Creating a Content-Type using a feature is relatively straight forward, and we’ll do that in another post by building on the site columns built during this article.

Posted in Software Development | Tagged: , , | 2 Comments »