Wednesday, March 24, 2010

SharePoint 2010: Share Content Type with Managed Metadata Service

Till SharePoint 2007 there’s no way to manage Content type centrally. To share Content Type across site collection level or web application level in SharePoint 2007, you need to copy the same content type in different site collections or web applications. Another way could be to use web feature to enable content type across site collections or web applications.But the good news is that SharePoint 2010 has provided out of the box support for sharing Content types across farm level.

Publish Content Type

The Enterprise Metadata Management Service or Metadata Service (in short), allows to share Metadata and Content Type across the farm. To share Content types you need to define the content type(s) in a site collection (say CTSiteCollection). You can consider the site collection (CTSiteCollection) as the place where you’ll put all your related content types to share across farm. This site collection,which is used to host content types or sharing,is called Content Type Hub.

1. Create a new content type in a site collection that you want to share

First select a site collection as a place to host your content types. You will define your content types to share across, in this site collection. You can create a new site collection or use an existing one. Once you have an site collection in your hand, create a content type and for this discussion let’s name it MyContentType.

2. Activate feature to enable content publishing in site collection:

The site collection, which will host content types for sharing, is known as content type hub. To enable your site collection to be a content type hub you need to  activate a feature known as “Content Type Syndication Hub”. Once you have activated this feature, you will find an option “Manage Publishing for this content type” in your content type setting page as shown below:

image

Figure 1: Content type publishing option will be available on activating feature “Manage Publishing for this content type”

3. Bind content type hub with a metadata service.

Go to the central administration => Application Management. Then select your metadata service or create a new one. You will find there are two entries for each metadata service. One is service and another is connection as shown below.

image

Figure 2: Metadata service and connection

Configure Metadata Service: Now click on the metadata service (but not on the name as this will bring you to ‘Term Store Management Tool’) and then click Properties button from the ribbon on the top of the page. On the properties window there’s an option for Content Type Hub and here put the url of your site collection you have selected/created (for example, http://server:port/ or http://server:port/sites/mysite). Now your metadata service is ready to be used by metadata service

Configure Metadata Connection: To configure metadata connection, go to metadata service connection properties and ensure that the connection consumes the content type hub as shown below:

image

Figure 3: Metadata connection settings for consuming content type hub.

4. Publish the content type

Now you go back to the site collection (which is used as content type hub) and go to content type settings page. Here in this page you will get the option “Manage publishing for this content type” option. If you don’t get the option activate feature “Content Type Syndication Hub” as described in step 2. Now click on the link “Manage Publishing for this content type” and ensure the ‘publish’ option is selected and then click OK as shown below.

image

Figure 4: Content type publishing page

Now you are done but you may not find the content type available immediately as the publishing is managed by a timer job which usually run in every 15 minutes. To run the job immediately you can navigate to Central Admin site => Monitoring => Review Job definitions. Then you can run the ‘Content Type Hub’ immediately.

 

Use Published Content Type

1. Associate Metadata connection to your web application:

To use published content type from a different web application or site collection, you need to associate the metadata connection with the web application. To do so navigate to the Central Administration => Application Management => Configure service application associations. Then click on the site link and you’ll be provided an “Configure Service Application Associations” window. From there ensure you have selected the metadata service which you configured for content type.

After associating the metadata service with web application, you may not get the content type immediately as a timer job will sync the content type between content type hubs and consumers. To run the job immediately navigate to Central Admin site => Monitoring => Review Job definitions. Then find the job “Content Type Subscriber”. There are one job for each subscriber. Find the job for your site and run immediately.

2. Use Content Type

To use content type go to the site where you want to use the content type. But before that make sure you have associated the metadata connection for this web application as described in step 1. Now create a new list in the consumed web site and go to list settings. Then click ‘Advanced settings’ and select yes for ‘Allow Management of Content Types’. Now click ok and move back to settings page and you should get the ‘Content Types’ option as shown below:

image

Figure 5: Content Type settings for list/library

Now you can click on ‘Add from existing site Content Types’ and you should get the consumed content type ‘MyContentType’ as we created at step 1 under section ‘Publish Content Type’. If you don’t find the content type defined in content type hub site, there’s something wrong. To troubleshoot the problem move to the next section.

 

Troubleshoot, if Content type is not available in the consumed web site

To find if there’s any problem in publishing content type, go to site where you have defined the content type. Then go to site settings => “Content type Publishing“. Here you will find options for viewing error log. Though the error log is not so much user friendly but you may get the gist of problem.

If there’s nothing in the error log then make sure the timer job (which actually causes the content types to publish/consume) is run. Timer jobs run periodically, may be after 15 minutes. To run the job immediately, go to Central Administration => Monitoring => Review Job Definitions. Find all jobs related to Content types. After running all jobs check if content type is available. If not available then search for error in the error log as described in previous paragraph.

Saturday, March 20, 2010

SharePoint 2010: Hierarchical and Equivalent taxonomy types are supported

If you don’t have basic idea of taxonomy terms in SharePoint please read my previous related article. As we know SharePoint 2010 has added support for taxonomy with a feature called Enterprise Metadata Management or EMM. There are three basic kids of relationship in taxonomy:
1. Hierarchical: In this type of relation, terms (items in taxonomy) are connected hierarchically. For example Books can be categorized as Novel, Drama, Science Fiction etc. Then Novel can be subcategorized in different ways. So these represent hierarchical relationship in taxonomy.
2. Equivalent: Two terms can be equivalent. For example TV and Television are equivalent.
3. Associative: In this case terms are related anyway. For example Television and Radio are related in some context.

How taxonomy types/relations are used in SharePoint?

Taxonomy is used mainly to tag content but how these three taxonomy types are used in SharePoint for tagging content. For your information only Hierarchical and Equivalent content types are supported in SharePoint.  For example you are uploading a video in a site developed with SharePoint. You want to tag the video with metadata so that user can find the video easily using search functionality. While you will upload the video you can select from existing term from taxonomy or enter a new/existing term(s) for this video. Say you are uploading a BBC TV program on famine in Africa. Then you can select/enter terms: TV, BBC, Famine, Africa. Now consider how we can organize these terms with taxonomy supports in SharePoint. Take a look at the figure 1 where you can find three sections:
1. Hierarchical taxonomy section: User will select from a predefined set of terms from taxonomies.
2. Upload video section: Upload the content, i.e., video.
3. Keyword (equivalent taxonomy) section: User will enter and as he/she will enter, suggestions will come up. As you can see in the image below, while user is typing BBC suggestion is coming up with British Broadcasting Service (BBC). Here BBC and British Broadcasting Service are equivalent term in the taxonomy. So whether user enter BBC or British Broadcasting Service, a single term (may be BBC or British Broadcasting Service) will be selected. So if another user search video by BBC or British Broadcasting Service, this will be selected as BBC and British Broadcasting Service are the same.
image
Figure 1: How Hierarchical and Equivalent taxonomy can be used to tag content (i.e., video)

How to define Hierarchical and Equivalent terms in SharePoint 2010

To define a term in SharePoint 2010, go to Center administration site and navigate to ‘Application Management’. Then navigate to ‘Manage Service Applications’ under Service Applications. Then select the metadata service and you will be navigated to ‘Term Store Management Too’. You can easily define hierarchical terms. However to define any Equivalent terms, define any equivalent term first. For our example of ‘Television’, define Television first. Then you will find an option for defining equivalent term for television where you can define TV.
image
Figure 2: Define Equivalent Term
As shown in the figure 2, you can define multiple equivalent terms for one term.

Conclusion

I had read in few blogs that SharePoint 2010 supports hierarchical and equivalent taxonomy but I had not found how equivalent term is defined. In this post I have tried to show how you can define the equivalent as well as hierarchical terms with term management tool.

Monday, March 15, 2010

SharePoint 2010: Manage (Create/Delete/Rename) list folders with Client Object Model (OM)

SharePoint 2010 Client Object models allows almost everything to do from client side with Client OM. In this post I’ll show how to Add, remove and rename SharePoint list folders with Client OM. I have been using Managed client OM. However you can get the idea of how to use this code from Client OM for silverlight and Client OM for ECMAScript. Also the code snippet I have posted here in this post is will only work for list folders. However by changing few parts you can make it working for library.

Create or Add Folder

The following code snippet shows how to add a new folder in a list.

public void CreateFolder(string siteUrl, string listName, string relativePath, string folderName)
{
using (ClientContext clientContext = new ClientContext(siteUrl))
{
Web web = clientContext.Web;
List list = web.Lists.GetByTitle(listName);

ListItemCreationInformation newItem = new ListItemCreationInformation();
newItem.UnderlyingObjectType = FileSystemObjectType.Folder;
newItem.FolderUrl = siteUrl + "/lists/" + listName;
if (!relativePath.Equals(string.Empty))
{
newItem.FolderUrl += "/" + relativePath;
}
newItem.LeafName = folderName;
ListItem item = list.AddItem(newItem);
item.Update();
clientContext.ExecuteQuery();
}
}

As shown in the code snippet above, to add a folder with Client OM you need to create an instance of ListItemCreationInformation and set it’s UnderlyingObjectType to Folder. You can also set the path where to create the folder by setting the FolderUrl property value.

You can call the CreateFolder method as shown below which will create a folder ‘myfolder’ in the list ‘mylist’. As relative path is passed empty, the folder will be added in the root folder of the list.

helper.CreateFolder("http://server", "mylist", "", "myfolder");

However you can add folder in any subfolder of the list. For example the following code snippet will add the folder ‘mysubfolder’ in the list ‘mylist’ under the folder ‘myfolder’.

helper.CreateFolder("http://server", "mylist", "myfolder", "mysubfolder");

 

Delete or Remove folder

To delete folder with Client OM, you need to query the folder first. You can do so by using instantiate a CamlQuery object and executing it. The following code snippet shows how to delete folder.

public void DeleteFolder(string siteUrl, string listName, string relativePath, string folderName)
{
    using (ClientContext clientContext = new ClientContext(siteUrl))
    {
        Web web = clientContext.Web;
        List list = web.Lists.GetByTitle(listName);

        CamlQuery query = new CamlQuery();
        query.ViewXml = "<View Scope=\"RecursiveAll\"> " +
                        "<Query>" +
                            "<Where>" +
                                "<And>" +
                                    "<Eq>" +
                                        "<FieldRef Name=\"FSObjType\" />" +
                                        "<Value Type=\"Integer\">1</Value>" +
                                     "</Eq>" +
                                      "<Eq>" +
                                        "<FieldRef Name=\"Title\"/>" +
                                        "<Value Type=\"Text\">" + folderName + "</Value>" +
                                      "</Eq>" +
                                "</And>" +
                             "</Where>" +
                        "</Query>" +
                        "</View>";

        if (relativePath.Equals(string.Empty))
        {
            query.FolderServerRelativeUrl = "/lists/" + listName;
        }
        else
        {
            query.FolderServerRelativeUrl = "/lists/" + listName + "/" + relativePath;
        }

        var folders = list.GetItems(query);

        clientContext.Load(list);
        clientContext.Load(folders);
        clientContext.ExecuteQuery();
        if (folders.Count == 1)
        {
            folders[0].DeleteObject();
            clientContext.ExecuteQuery();
        }
    }
}

As shown in the above code snippet, I first instantiated the CamlQuery with the query to search the folder. The only trick to search folder only is to use the following part in the CAML query. If you want to use files only then use value 0 instead of 1.

<FieldRef Name="FSObjType" />
<Value Type="Integer">1</Value>

However you can use “CamlQuery.CreateAllFoldersQuery()” to get the query to search all folders. After preparing the CAML query you need to execute this against the list. For optimization you can specify the field names you want to load inside the Load method.

Rename Folder

To rename folder you need to get the folder item first. To get the folder you need to query the list for folder as shown in section “Delete or Remove Folder”. The following code snippet shows how to rename folder.

public void RenameFolder(string siteUrl, string listName, string relativePath, string folderName,string folderNewName)
{
    using (ClientContext clientContext = new ClientContext(siteUrl))
    {
        Web web = clientContext.Web;
        List list = web.Lists.GetByTitle(listName);

        string FolderFullPath = GetFullPath(listName, relativePath, folderName);

        CamlQuery query = new CamlQuery();
        query.ViewXml = "<View Scope=\"RecursiveAll\"> " +
                        "<Query>" +
                            "<Where>" +
                                "<And>" +
                                    "<Eq>" +
                                        "<FieldRef Name=\"FSObjType\" />" +
                                        "<Value Type=\"Integer\">1</Value>" +
                                     "</Eq>" +
                                      "<Eq>" +
                                        "<FieldRef Name=\"Title\"/>" +
                                        "<Value Type=\"Text\">" + folderName + "</Value>" +
                                      "</Eq>" +
                                "</And>" +
                             "</Where>" +
                        "</Query>" +
                        "</View>";

        if (relativePath.Equals(string.Empty))
        {
            query.FolderServerRelativeUrl = "/lists/" + listName;
        }
        else
        {
            query.FolderServerRelativeUrl = "/lists/" + listName + "/" + relativePath;
        }
        var folders = list.GetItems(query);

        clientContext.Load(list);
        clientContext.Load(list.Fields);
        clientContext.Load(folders, fs => fs.Include(fi => fi["Title"],
            fi => fi["DisplayName"],
            fi => fi["FileLeafRef"]));
        clientContext.ExecuteQuery();

        if (folders.Count == 1)
        {

            folders[0]["Title"] = folderNewName;
            folders[0]["FileLeafRef"] = folderNewName;
            folders[0].Update();
            clientContext.ExecuteQuery();
        }
    }
}

As shown in the code snippet above, I have specified the field names (Title, DisplayName, FileLeafRef) in the load method to ensure that these fields are populated on the client side. Once the query is executed by invoking the ClientContext.ExecuteQuery, the folders are populated. To rename folder you need to modify at least two properties (Title, FileLeafRef). Finally you need to call the update method and to send the change back to the server need to invoke the ExecuteQuery method

Search Folder

You can enumerate a list for folders as shown below:

public void SearchFolder(string siteUrl, string listName, string relativePath)
{
    using (ClientContext clientContext = new ClientContext(siteUrl))
    {
        Web web = clientContext.Web;
        List list = web.Lists.GetByTitle(listName);

        string FolderFullPath = null;

        CamlQuery query = CamlQuery.CreateAllFoldersQuery();

        if (relativePath.Equals(string.Empty))
        {
            FolderFullPath = "/lists/" + listName;
        }
        else
        {
            FolderFullPath = "/lists/" + listName + "/" + relativePath;
        }
        if (!string.IsNullOrEmpty(FolderFullPath))
        {
            query.FolderServerRelativeUrl = FolderFullPath;
        }
        IList<Folder> folderResult = new List<Folder>();

        var listItems = list.GetItems(query);

        clientContext.Load(list);
        clientContext.Load(listItems, litems => litems.Include(
            li => li["DisplayName"],
            li => li["Id"]
            ));

        clientContext.ExecuteQuery();

        foreach (var item in listItems)
        {

            Console.WriteLine("{0}----------{1}", item.Id, item.DisplayName);
        }
    }
}

As shown above, the CamlQuery.CreateAllFoldersQuery method generate a query which will query folders. You can pass that query in any list’s GetItems method to get the folders of that list. You can also specify the root folder from which to start searching for folder by specifying the CamlQuery.FolderServerRelativeUrl property.

 

Conclusion

Client OM has opened a new window on SharePoint programming. I have found it very useful as it exposed almost full power of SharePoint on client side.

Wednesday, March 3, 2010

Software Development: 80-20 principle or edge case

Few days back I was reading an article of David S. Platt on MSDN. David was taking about something like 80-20 principle. As per his writing, we can make happy 99 people but not the 100th person. In his first article in MSDN he stated a feature (designed for 110th user but not for another 99 users) to dock the file menu bar as bug.

In his second article, getting some arguments against his first article, he reiterated again about the point edge cases where only a few people will be benefitted. These two articles are must-read for people who is working in management as well as for developers.