SharePoint Dragons

Nikander & Margriet on SharePoint

Content type galore

Never one to avoid the well treaden path, and hoping to find new pieces of information along the way or at least presenting a comfortable holistic view of the topic at hand: our new article is about content types. Walk along, if you please?

Creating a content type programmatically

If you create a new custom content type, it always need to inherit from an existing base content type. Programmatically, this means you can retrieve an existing content type, and use that during the creation of your own content type, like so:

using (var site = new SPSite(“http://astro”))
{
    using (var web = site.OpenWeb())
    {
        try
        {
            string ctname = “My CT”;
            SPContentType myCt = web.ContentTypes[ctname];

            // Create a new content type if it doesn’t exist yet.
            if (myCt == null)
            {
                var ctParent = web.ContentTypes[“Item”];
                myCt = new SPContentType(ctParent, web.ContentTypes, ctname);

                // To improve manageability, define a description and group
                myCt.Description = “My test content type”;
                myCt.Group = “Lois and Clark Content Types”;

                // Now add the content type to a target site
                web.ContentTypes.Add(myCt);
            }
        }
        catch (Exception err)
        {
            Console.WriteLine(err.Message);
        }
    }

Or you can retrieve the base content type via the SPBuiltInContentTypeId class that retrieves the content type ids for every built-in content type. It should be a little faster, and it certainly allows for less room when it comes to making typos. It goes like this:

var ctParent = web.ContentTypes[SPBuiltInContentTypeId.Item];

Once added to the content types collection of a SharePoint site, you’re free to modify your custom content type according to your specific needs.  For instance, you can change the display name of the Title field.

var title = myCt.FieldLinks[“Title”];
title.DisplayName = “my custom title”;

Or, you can add new fields based on existing site columns:

// Add new fields that are based on existing site columns:
if (myCt.FieldLinks[“WorkPhone”] == null)
{
    myCt.FieldLinks.Add(new SPFieldLink(web.Fields.GetFieldByInternalName(“WorkPhone”)));
}

Don’t forget to save after you’re done:

// Finally, save changes.
myCt.Update();

This causes the complete code listing to look like this:

using (var site = new SPSite(“http://astro”))

{
    using (var web = site.OpenWeb())
    {
        try
        {
            string ctname = “My CT”;
            SPContentType myCt = web.ContentTypes[ctname];

            // Create a new content type if it doesn’t exist yet.
            if (myCt == null)
            {
                var ctParent = web.ContentTypes[SPBuiltInContentTypeId.Item];
                myCt = new SPContentType(ctParent, web.ContentTypes, ctname);

                // To improve manageability, define a description and group
                myCt.Description = “My test content type”;
                myCt.Group = “Lois and Clark Content Types”;

                // Now add the content type to a target site
                web.ContentTypes.Add(myCt);
            }

            // Change an existing field
            var title = myCt.FieldLinks[“Title”];
            title.DisplayName = “my custom title”;

            // Add new fields that are based on existing site columns:
            if (myCt.FieldLinks[“WorkPhone”] == null)
            {
                myCt.FieldLinks.Add(new SPFieldLink(web.Fields.GetFieldByInternalName(“WorkPhone”)));
            }

            // Finally, save changes.
            myCt.Update();
        }
        catch (Exception err)
        {
            Console.WriteLine(err.Message);
        }
    }
}

Having that of our chests, let’s forget about creating content types programmatically, and see how to do it declaratively, the CAML way. Wouldn’t you agree that doing something in CAML always feels like you’re doing something “fundamentally SharePointistic”? It feels like talking to your forefathers from the day of yore.

Creating a content type declaratively

Or you can create one declaratively as described at http://msdn.microsoft.com/en-us/library/gg295290.aspx. If you do so, you need to understand the structure of the content type id. It looks like this:

[parent id][00][guid]

Previously, we’ve used the item content type as the base type for our custom content type. As it happens, the item content type is the root of all content types. It’s id is 0x01 and you’ll see it a lot. 00 is just a delimiter between the parent content type id. The guid of your custom content type id needs to be created by yourself. You can use Tools > Create GUID if you’ve installed VC++, or create a simple app that uses the line Guid.NewGuid().ToString(); to do it yourself.

So, in this example:

0x01004edcc88144e24fabab267fd72e667051

Here, you see that we’ve created a content type with the guid 4edcc88144e24fabab267fd72e667051 that inherits from the item content type. As you can imagine, once you get deeper down the inheritance levels, the content type id’s can get quite large. Because of that, you can keep the content type id small by simply adding two hexadecimal digits (but not “00”) at the end. See http://msdn.microsoft.com/en-us/library/aa543822(v=office.12).aspx for more info. If you follow this way a child of content type 0x01004edcc88144e24fabab267fd72e667051 gets the following id:

0x01004edcc88144e24fabab267fd72e66705101

And another becomes:

0x01004edcc88144e24fabab267fd72e66705102

Best practice prescribes that if you inherit a content type from a SharePoint content type or a 3rd party, you should start appending the id with the format “00” and a GUID. After that, if you’re inheriting from your own content types use the minimal form and append two hexadecimal digits.

Please note: We cheated a little here. As far as we know, this is not an officially MS prescribed best practice, it is ours. You should follow it nevertheless :-)!

The aforementioned MSDN article (http://msdn.microsoft.com/en-us/library/gg295290.aspx) describes how to add a choice field. If you follow it, you’ll already be able to verify that the schema for the CAML defined in elements.xml (caml.net.intellisense.xsd) is far from complete and says “The Inherits attribute is not declared.” For now, accept this as a fact of life and hope that it’ll improve in the future. Or don’t, and download CAML.NET IntelliSense: https://sharepointdragons.com/2011/12/13/intellisense-for-caml/, it’ll add IntelliSense-like support when editing CAML files.

If you have followed the MSDN example, take some time to look at the elements.xml file. The choice field was easy to add (uh, copy) but what about the other field types? Next, we’ll discuss how to create each and every one of them.

But if you have written them, I bet you have seen yourself trying various elements in the elements.xml file(s), guessing, and heck even reverse engineering parts of sharepoint that ship with sharepoint.

Here is a quick trick.

You can export the entire list schema as CAML using the following –

http://yoursiteurl/_vti_bin/owssvr.dll?Cmd=ExportList&List=%7YourListGUID%7D

For example, choose List Settings in the browser to obtain your list GUID:

http://astro/_vti_bin/owssvr.dll?Cmd=ExportList&List=%7BD093BFE5%2D282E%2D41E9%2D99A2%2D3CD9AE11A704%7D

This is quite awesome, because it straight off gives you all the Fields, Views, and Content Types involved. The XML can still be quite verbose, so it pays of to check out which of the settings you want to keep or change. From there, you can copy CAML fields in your own elements.xml file.

Anyone who hasn’t read up on the URL protocol for calling owssvr.dll in the WSS SDK is really missing out.

Choice field

A choice field is of type Choice, and allows you to define multiple elements in the <CHOICES> section.

<Field ID=”{30C3D21A-A7C9-410E-A896-82875475F697}” Name=”ReviewStatus” DisplayName=”Review Status” Type =”Choice” >
    <CHOICES>
      <CHOICE>Review Required</CHOICE>
      <CHOICE>Review in Progress</CHOICE>
      <CHOICE>Review Completed</CHOICE>
    </CHOICES>
  </Field>

Please note: You do have to create a new GUID and use that as the id for the field. Again, you could use Tools > Create GUID to do this.

You’ll also have to add a field reference pointing to the field:

<FieldRef ID=”{30C3D21A-A7C9-410E-A896-82875475F697}” Name=”ReviewStatus” DisplayName=”Review Status”  />

Single line of text field

A single line of text has the type Text and has a default max length of 255. Although you’re better off using CAML.NET IntelliSense to remember this stuff for you, we still like to point out that ColName refers to a name in the underlying database structure. If you specify it, the name has to match. If you omit the attribute, the server generates a new non-colliding column name. The RowOrdinal column specifies the database location for the field. So, although both ColName and RowOrdinal columns can be omitted without trouble, it certainly seems more efficient to reuse existing settings.

The field definition and reference look like this:

<Field ID=”{5D956B33-993C-481E-9EBB-FAC949FCE34C}” Name=”MySText” DisplayName=”My custom single line of text” Type=”Text” MaxLength=”255″ ColName=”nvarchar3″ RowOrdinal=”0″ />

<FieldRef ID=”{5D956B33-993C-481E-9EBB-FAC949FCE34C}” Name=”MySText” DisplayName=”My custom single line of text”  />     

Multiline text field

Multiline fields are of type Note. If the RichText attribute is set to true, the field displays rich text formatting. The RichTextMode attribute defines specifics about the rich text settings and is set to FullHtml by default. It corresponds to the SPRichTextMode enumeration (see http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.sprichtextmode.aspx) which has the following members:

  • Compatible, displays plain text, or display rich text with bold, italic, or text alignment. Value = 0.
  • FullHtml, displays enhanced rich text, including pictures, tables, and hyperlinks. Value = 1.
  • HtmlAsXml, displays HTML as XML. Value = 2. This value is not supported by multiline text fields.
  • ThemeHtml, displays HTML with inline style specifications. This value is not supported by multiline text fields.

The IsolateStyles attribute instructs the server to rewrite the HTML (that is generated by the rich text editor) so that it doesn’t conflict with the rendering of the surrounding page. The default value of the NumLines attribute is 6 and corresponds to the number of lines shown in the HTML text area representing the multiline text field. It doesn’t limit the number of lines users are allowed to enter.

The field definition and reference look like this:

<Field ID=”{79557C0C-99AC-4157-A9EC-F98347AD91AB}” Name=”MyMText” DisplayName=”My multiliner” Type=”Note” RichText=”TRUE” RichTextMode=”FullHtml” IsolateStyles=”TRUE” NumLines=”6″ ColName=”ntext2″ RowOrdinal=”0″ />

Yes/No field

The Yes/No field presents a checkbox to the end user and is of type Boolean. The <Default> element allows you to choose the default value for this field.

The field definition and reference look like this:

<Field ID=”{FCFB7503-CF6F-41BF-A3C8-1FC779728148}” Name=”MyYesNo” DisplayName=”MyYesNo field” Type=”Boolean” ColName=”bit1″ RowOrdinal=”0″>
  <Default>1</Default>
</Field>

<FieldRef ID=”{FCFB7503-CF6F-41BF-A3C8-1FC779728148}” Name=”MyYesNo” DisplayName=”MyYesNo field” />

People (or group) picker field

The People picker (which is also used to select groups)  is of type User. It retrieves those users from the UserInfo database table in the SharePoint content database, a fact that is reflected in the List attribute. The ShowField attribute determines which field name gets displayed. If you don’t specify a value here a hyperlinked text from the Title field of the record in the external list is displayed. By default, the value of ImnName is used. The ImnName value doesn’t directly correspond to a column in the UserInfo table, but refers to a “Name with presence” (name including presence awareness symbol), a computed field generated by SharePoint when used. The UserSelectionMode attribute allows you to specify whether you’ll only allow users to pick users, or both users and groups. The UserSelectionScope determines the scope within which you can select users. If it’s value is set to 0, there are no restrictions. If it has a value greater than zero, it’s value refers to the id of a specific SharePoint group, and you’ll only be allowed to select users within that SharePoint group.

The field definition and reference look like this:

<Field ID=”{6BD0C2BC-B5F4-4874-AED5-8AB3AA2CBBC9}” Name=”MyPeople” DisplayName=”My People or group picker” Type=”User” List=”UserInfo” ShowField=”ImnName” UserSelectionMode=”PeopleOnly” UserSelectionScope=”0″ ColName=”int1″ RowOrdinal=”0″ />

<FieldRef ID=”{6BD0C2BC-B5F4-4874-AED5-8AB3AA2CBBC9}” Name=”MyPeople” DisplayName=”My People or group picker” />

Elements.xml

The resulting Elements.xml file containing all the fields that were discussed looks like this:

<?xml version=”1.0″ encoding=”utf-8″?>
<Elements xmlns=”http://schemas.microsoft.com/sharepoint/”>
 
  <Field ID=”{FCFB7503-CF6F-41BF-A3C8-1FC779728148}” Name=”MyYesNo” DisplayName=”MyYesNo field” Type=”Boolean” ColName=”bit1″ RowOrdinal=”0″>
    <Default>1</Default>
  </Field>

  <Field ID=”{79557C0C-99AC-4157-A9EC-F98347AD91AB}” Name=”MyMText” DisplayName=”My multiliner” Type=”Note” RichText=”TRUE” RichTextMode=”FullHtml” IsolateStyles=”TRUE” NumLines=”6″
         ColName=”ntext2″ RowOrdinal=”0″ />

  <Field ID=”{5D956B33-993C-481E-9EBB-FAC949FCE34C}” Name=”MySText” DisplayName=”My custom single line of text” Type=”Text” MaxLength=”255″ ColName=”nvarchar3″ RowOrdinal=”0″ />
 
  <Field ID=”{30C3D21A-A7C9-410E-A896-82875475F697}” Name=”ReviewStatus” DisplayName=”Review Status” Type=”Choice” >
    <CHOICES>
      <CHOICE>Review Required</CHOICE>
      <CHOICE>Review in Progress</CHOICE>
      <CHOICE>Review Completed</CHOICE>
    </CHOICES>
  </Field>

  <Field ID=”{6BD0C2BC-B5F4-4874-AED5-8AB3AA2CBBC9}” Name=”MyPeople” DisplayName=”My People or group picker” Type=”User” List=”UserInfo” ShowField=”ImnName”
         UserSelectionMode=”PeopleOnly” UserSelectionScope=”0″ ColName=”int1″ RowOrdinal=”0″ />

  <!– Parent ContentType: Item (0x01) –>
  <ContentType ID=”0x01004edcc88144e24fabab267fd72e667051″
               Name=”MyContentTypeProject – ContentType1″
               Group=”Custom Content Types”
               Description=”My Content Type”
               Inherits=”TRUE”
               Version=”0″>
    <FieldRefs>
      <FieldRef ID=”{30C3D21A-A7C9-410E-A896-82875475F697}” Name=”ReviewStatus” DisplayName=”Review Status” />
      <FieldRef ID=”{5D956B33-993C-481E-9EBB-FAC949FCE34C}” Name=”MySText” DisplayName=”My custom single line of text” />
      <FieldRef ID=”{79557C0C-99AC-4157-A9EC-F98347AD91AB}” Name=”MyMText” DisplayName=”My multiliner” />
      <FieldRef ID=”{FCFB7503-CF6F-41BF-A3C8-1FC779728148}” Name=”MyYesNo” DisplayName=”MyYesNo field” />
      <FieldRef ID=”{6BD0C2BC-B5F4-4874-AED5-8AB3AA2CBBC9}” Name=”MyPeople” DisplayName=”My People or group picker” />
    </FieldRefs>
  </ContentType>
</Elements>

That’s all folks!

One response to “Content type galore

  1. Filipe October 21, 2012 at 10:16 pm

    I couldn’t find this information elsewhere as understandable as in this article, thank you very much!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: