Monday, 18 April 2011

Specifying Content Types from the SharePoint 2010 Silverlight Client Object Model

Cross-posted from Jason Lee's Blog

A few weeks ago, I wrote about how you can use the Silverlight client object model to upload files to a SharePoint document library. One of the limitations of this process is that it doesn't allow you to specify a content type or provide any metadata for the document you're uploading. In this post, I look at how you can programmatically provide this missing information.

As with most client-side operations for SharePoint 2010, the process is a little more complex from a Silverlight client than from a managed .NET client, as many useful methods and properties are unavailable. From a Silverlight client, you need to use the following high-level steps:

  • Upload the file
  • Retrieve the list item corresponding to the file
  • Update the field values of the list item to set the content type and any other required metadata

Let's take a look at how this works in code. Because we're working with a document library, you must upload the file as the first step –SharePoint won't allow you to create a list item first and upload the document when you're finished providing metadata. I covered uploading a document in a fair amount of detail last time, so let's assume we've done that already. The next step is to retrieve the list item that SharePoint created when we uploaded the document.

Since we need to execute more than one query, it's easier to queue our logic to run on a background thread. This means we can execute queries synchronously rather than creating multiple nested callbacks, which get difficult to untangle after a while.

ClientContext context = ClientContext.Current; System.Threading.ThreadPool.QueueUserWorkItem(
   new System.Threading.WaitCallback(UpdateMetadata), context);

In the callback method, the first step is to submit a CAML query that retrieves the list item corresponding to our document. Notice that we also load the collection of available content types. You'll see why in a bit.

private void UpdateMetadata(object state)
{
   ClientContext context = (ClientContext)state;
   Web web = context.Web;
   List list =
      context.Web.Lists.GetByTitle("My Document Library");
   CamlQuery query = new CamlQuery();
   query.ViewXml = @"
      <View>
         <Query>
            <Where>
               <Eq>
                  <FieldRef Name='FileLeafRef'/>
                  <Value Type='Text'>Sample.txt</Value>
               </Eq>
            </Where>
         </Query>
         <RowLimit>10</RowLimit>
      </View>";
   ListItemCollection items = list.GetItems(query);
   context.Load(items);
   ContentTypeCollection contentTypes =
      context.Web.AvailableContentTypes;
   context.Load(cts);
   context.ExecuteQuery();

Let's assume we want to assign an arbitrary content type named "Chapter" to our list item. To set the content type of a list item, we need to set the value of the ContentTypeId field. In the Silverlight client object model, the ContentTypeCollection class doesn't allow you to use the name of the content type as an indexer. Instead, we can use a simple LINQ expression to get the ID of our Chapter content type.

   var ctid = from ct in contentTypes
              where ct.Name == "Chapter"
              select ct.Id;

We can now set the content type of our document and provide any required metadata.

   ListItem item = items[0];
   item["ContentTypeId"] = ctid;
   item["PublishingContactName"] = "Jason L";
   item["PublishingContactEmail"] = "jason@example.com";
   item.Update();
   context.ExecuteQuery();
}

In a real-world application, you'd obviously need to check that your query returned one unique list item, build in error handling, and so on. However, hopefully this provides enough information to get you started.