Pages

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.

Sunday, February 21, 2010

SharePoint 2010: Client Object Model for JavaScript (ECMAScript)

As I said in my first post on Client Object Model (OM), there are three sets of Client OM: Managed .net client, Silverlight and ECMAScript. Lately I have posted an article on how to use Client OM from Managed .Net Client.Today I’ll go through Client OM for JavaScript.  ECMAScript Client OM is SharePoint 2010 client object model extension for using with JavaScript or JScript. Few points to notice about ECMAScript Client OM:

  • ECMAScript object model can only be used in SharePoint sites. So you can’t use this object model in an asp.net site to access SharePoint resources deployed in another url as this is cross-site scripting and not allowed.
  • You can’t use this object model in a SharePoint site to access resources in different SharePoint sites(i.e., different urls). For example, from mysite.mysp.com you can access resources in yoursite.yoursp.com using ECMAScript client OM. This is also cross-site scripting.
  • You can use JQuery with ECMAScript Client OM and for this you don’t need to do some extra work. All you need to do to use JQuery is to add reference to JQuery.js file and start using JQuery.
  • You can use this ECMAScript Client OM in web part pages or application pages (aspx pages) by referencing a javascript file (SP.js). You don’t need to add reference to the file manually rather use <SharePoint:ScriptLink Name=”sp.js” ………. />. The file is located on the path “Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS”
  • To update with JavaScript, you need to add a FormDigest tag in your page for security purpose. I’ll explain it later in details.

 

Use ECMAScript Library

At first use Visual Studio 2010 to create a SharePoint web part project. As a result, VS2010 will open a ascx control for you on the designer. 

1. Add reference to js file:

To use Client OM, add an entry like below in your web part ascx control. For your information, there’s an debug version of the sp.js called sp.debug.js which you can use for debugging but should not be used in production.

<SharePoint:ScriptLink Name="SP.js" runat="server" OnDemand="true" Localizable="false" />

Here, OnDemand means whether the sp.js file need to be loaded on demand (not in page load) or not.

2. Add FormDigest tag:

If your code modifies SharePoint content add a FormDigest control inside your page. The FormDigest add a security token inside your page based on user, site and time. Once the page is posted back the security token is validated. Once the security token is generated it’s valid for a configurable amount of time. Add the FormDigest inside <form>…</form> tag, as shown below:

<SharePoint:FormDigest runat="server" />
 
For more information on FormDigest follow the links below:

3. Use Client OM to retrieve data:

Now you can use SharePoint ECMAScript library. Lets dissect the code snippet below. The first thing in using this library is that you need to get the ClientContext (just like SPContext). Then the context.get_web returns the current web (just like SPContext.Current.Web). Then client context’s load method is invoked passing the web object. Then the executequery method is invoked asynchronously passing two functions: onSuccess and OnFailed which will be called on success and fail correspondingly.

<script type="text/javascript">
function getWebProperties() {
var ctx = new SP.ClientContext.get_current();
this.web = ctx.get_web();
ctx.load(this.web);
ctx.executeQueryAsync(Function.createDelegate(this, this.onSuccess), Function.createDelegate(this, this.onFail));
}
function onSuccess(sender, args) {
alert('web title:' + this.web.get_title() + '\n ID:' + this.web.get_id() + '\n Created Date:' + this.web.get_created());
}
function onFail(sender, args) {
alert('failed to get list. Error:'+args.get_message());
}
</script>

By calling getWebProperties method from any web part, you can get the current web’s title, id and creation date.

4. Load minimal data you need:

In the above code snippet, the Ctx.load method is invoked with only one parameter (web). The load method will load all properties of the web object. But we are only using Id, Title and Created Date properties. If you know which properties you are interested in, you can pass the properties names in the load method and only those properties will be loaded. For example the following load method will return only ID, Title and Created Date.
 
ctx.load(this.web,'Title','Id','Created');

Remember, here the properties names are properties of SPWeb. You need to pass Title instead of title. The properties name uses CAML casing. You can get the full lists of ECMAScript namespaces, object, properties following the link on MSDN. The document is not final yet and may be changed. You can also look into the sp.debug.js file in the folder “Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS”, to get an idea of objects, properties and methods of ECMAScript Client OM.

5. Execute your JavaScript function after sp.js is loaded:

Sometimes you may need to execute your JavaScript (that uses ECMAScript Client OM) on page load in the browser. But since your JavaScript is using sp.js file and if the sp.js file is not loaded yet (since to lazy loading nature of sp.js), when your custom JavaScript will be executing, you’ll get your JavaScript function not executed. In this case you need to make sure your JavaScript code runs after sp.js finishes loading. You can do so by putting your JavaScript method call inside a js function as shown below:

ExecuteOrDelayUntilScriptLoaded(myjsfucntion, "sp.js");

Putting your JavaScript function (i.e., myjsfunction) inside the ExecuteOrDelyUntilScriptLoaded method delays your method call until the sp.js file is loaded.

6. Update with ECMAScript Library:

You can use the Client OM to update SharePoint contents. The following code snippet shows how to update web title.

<script type="text/javascript">
function updateTitle() {
var ctx = new SP.ClientContext.get_current();
this.web = ctx.get_web();
web.set_title('UpdatedTitle');
this.web.update();
ctx.executeQueryAsync(Function.createDelegate(this, this.onUpdate), Function.createDelegate(this, this.onFail));
}
function onUpdate(sender, args) {
alert('title updated');
}
function onFail(sender, args) {
alert('failed to update title. Error:'+args.get_message());
}
</script>

By calling the updateTitle method from any web part or SharePoint application pages, you can change the title of current web site (where the web part or application page is deployed). For your information, in ECMAScript Client OM, to get an property use get_propertyName and to set a property use set_propertyName. To update list with ECMAScript library you need to add FormDigest tag.

Use JQuery with ECMAScript

You can use JQuery with ECMAScript without any conflict. As usual, you need to add jquery.js file reference to your page/web part or in master page. Then you can use JQuery as like normal asp.net applications. But make sure that if you need to execute any JavaScript function on page load event, you put this inside ExecuteOrDelayUntilScriptLoaded function.

Deployment Consideration

SharePoint provides two sets of JavaScript file: minified and unminified/debug version. For example sp.js file is minified and sp.debug is minified and debug version. The default master page in SharePoint has a scriptmanager in the page and whose ScriptMode is set to auto, as a result the minified version of js file loaded. If you want to use debug version you can add the <deployment retail="false" /> in the <system.web> section of the web.config. In the production you need to remove this entry to make sure minified version is used. The ECMAScript supported in the following browsers:

  • Microsoft Internet Explorer 7.0 or greater.
  • Firefox 3.5 or greater
  • Safari 4.0 or greater

More Information

To get the full list of namespaces and Classes, you can download the SharePoint 2010 SDK or you can follow the link on MSDN.

Tuesday, February 16, 2010

SharePoint 2010: Managed .net Client with Client Object Model (OM)

The client Object Model for Managed .net application allows any .net managed application to communicate with SharePoint server without using any web service reference. Say you have a WPF application and from that WPF application you need to get data from SharePoint calendar. In the days of SharePoint 2007 you could read the calendar data from SharePoint through web service. If the out of box web service could not meet your demand then you needed to develop your custom web services. But with Client OM you can now program against SharePoint on the client side. In my first post on Client OM I had gone through the some basics. In this post I’ll cover how to use Client OM in managed .net applications (like windows, web, console etc).

Steps to use Client OM with Managed .net application

1. Create a new project (like console, wpf, web etc) in Visual Studio 2010. During creating the project make sure you have selected the target framework to 3.5. By default VS2010 uses .net framework 4 but Client OM will not function with .net framework. The following figure shows the “Add New Project” window with targeted framework selected as 3.5.

image

Figure 1: New Project dialog

2. To use Client OM with your project add reference to two following DLLs to the project from the directory C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI.

  • Microsoft.SharePoint.Client.dll
  • Microsoft.SharePoint.Client.Runtime.dll

3. Now you can use Client OM. The core object in the client OM is ClientContext. You’ll need to initialize the ClientContext with the SharePoint site url. Let’s dig dipper inside Client OM.

Use Client OM

Let’s have a look at the following code snippet:

public DateTime GetWebCreatedDate()
{
      using (ClientContext context = new ClientContext("http://myserver"))
      {
            Web web = context.Web;
           context.Load(web, w => w.Created);
           context.ExecuteQuery();
           return web.Created;
       }            
}

Code Snippet 1: Use Client OM to get web site creation date

Few points to notice about the code snippet that uses Client OM above:

a) The ClientContext is needed to initialize with SharePoint site’s url.

b) ClientContext.Load takes the SharePoint object to load as first parameter. The next parameters are the properties to be loaded for the object. As shown in the code snippet the load method is invoked asking only one attribute to load (Create Date).

c) To optimize data retrieval, the Client OM queue all requests to the SharePoint server till an invocation to the ExecuteQuery is made. So no data will be available from the server till the ExecuteQuery method is invoked. After finishing the ExecuteQuery method invocation the properties asked to load in the Load method is populated.

d) If you want to use a property that you are not asked to load in the ClientContext.Load method, you’ll get PropertyOrFieldNotInitializedException is thrown. The same exception will be thrown if you want to use any property of the Client object before calling the ExecuteQuery method.

In the above code snippet, if I tried to use web.Created property before context.ExecuteQuery, I would get PropertyOrFieldNotInitializedException. Even after calling ExecuteQuery, if you try to use any other properties that you have specified in load method (like web.Id or web.Title in above code snippet), you’ll get the same exception. To get more properties of web you need to provide the property/field name in the load method. For example, to load Title and Id you need to modify the load method as shown below:

context.Load(web, w => w.Created,w=>w.Title,w=>w.Id);

Similarly you can specify as much property as you need. However if you don’ t provide any second parameter to the load method as shown below, then all properties of the object will be loaded. The following load method will populate all fields of web object.

context.Load(web);

In real life scenario, we will barely need to use the above load method as it’ll send a large amount data to the client which is waste of network bandwidth and server resources.

 

Load properties/Fields selectively

As I have specified before, in Client OM you can specify the fields/properties to load. The load method of ClientContext works two ways:

1. For single item: For a single item the load methods take a series of arguments that specify the property to load for the item. For example the following few statements load one two and three properties correspondingly.

context.Load(web, w => w.Id);
context.Load(web, w => w.Id, w => w.Title);
context.Load(web, w => w.Id, w => w.Title,w => w.Created);


2. For List of items: To specify properties/fields to load for each item of the list, an extension method ‘include’ is used. As the following code snippet shows, a caml query is used to filter data from list. Then in the load method I have used ‘include’ extension method to specify the properties to be loaded.

ClientContext clientContext = new ClientContext("http://myserver");
Web web = clientContext.Web;
List list = web.Lists.GetByTitle("Employee");
CamlQuery query = new CamlQuery();
query.ViewXml = @"<View>
    <Query>
      <Where>
        <Eq>
          <FieldRef Name='FirstName'/>
          <Value Type='Text'>Sohel</Value>
        </Eq>
      </Where>
    </Query>
  </View>";

ListItemCollection listItems = list.GetItems(query);
clientContext.Load(listItems, li => li.Include(i => i["FirstName"], i => i["LastName"]));
clientContext.ExecuteQuery();

Code Snippet 2: Code snippet to showing Include extension method

 

Update List with Client OM

With Client OM, you can update list items. As shown in Code Snippet 3, you can get the data as soon as the first clientContext.ExecuteQuery is invoked. After that you can loop through all the list items and update the list item. To send back changes to the server again you need invoke ExecuteQuery method again. The following code snippet show how a list is filtered and then updated.

ClientContext clientContext = new ClientContext("http://myserver");
Web web = clientContext.Web;
List list = web.Lists.GetByTitle("Employee");
CamlQuery query = new CamlQuery();
query.ViewXml = @"<View>
    <Query>
      <Where>
        <Eq>
          <FieldRef Name='FirstName'/>
          <Value Type='Text'>Sohel</Value>
        </Eq>
      </Where>
    </Query>
  </View>";

ListItemCollection listItems = list.GetItems(query);
clientContext.Load(listItems, li => li.Include(i => i["FirstName"],i=>i["LastName"]).Where(i=>i["FirstName"].ToString()=="sohel");
clientContext.ExecuteQuery();


foreach (ListItem item in listItems)
{
    Console.WriteLine(item["FirstName"].ToString());
    item["FirstName"] = "sohel 1";
    item.Update();
}
clientContext.ExecuteQuery();
Code Snippet 3: Update with Client OM

The important thing to notice in the above code snippet is that the ClientContext.ExecuteQuery is invoked twice. Once to get list items from server. Then after modifying list item on the client side, we need to invoke the ExecuteQuery again to send data back to the server. To delete a list item you need to call the DeleteObject method of the list item. The following code snippet shows how you can delete an item.

foreach (ListItem item in listItems)
{
    item.DeleteObject();
}

 

Authentication with Client OM

To specify authentication in the client context you need to set the context.AuthenticationMode Property with either default, anonymous or forms. For forms authentication you also need to set a instance of FormsAuthenticationLoginInfo object to the property FormsAuthenticationLoginInfo. The following code snippet shows how to use form authentication with Client OM.

context.AuthenticationMode = ClientAuthenticationMode.FormsAuthentication;
context.FormsAuthenticationLoginInfo = new FormsAuthenticationLoginInfo 
{ 
       LoginName="username",
       Password="password",
};

 

Optimization in Client OM

Two methods play important roles in optimizing the Client OM. One is ClientContext.Load method. This method helps us to specify the properties of an object we want to retrieve. If this option would not be available then whenever we needed to load the Web object, a lot of unnecessary properties would be sent to the client. This would case heavy unnecessary traffic to the system. This would also cause the server to load too much unnecessary data and process it.

Another optimization is done with ExecuteQuery. Since all operations against ClientContext are queued till this method is called the number of round-trip to the server is reduced significantly. But with the introduction of ExecuteQuery, developers need to change their mindset as we can’t just call any field or property as we want rather we need to call ExecuteQuery first.

 

Deployment Consideration

When you use .net Managed Client OM you only need two extra assemblies (Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll). So your code will have no web services reference.

Friday, February 12, 2010

SharePoint 2007: Create web part page library, add a webpart page with webpart without using publishing feature

You can create a document library with several document templates for example, excel, word, webpart page etc. If you need to create a document library with ‘web part page’ document template programmatically then you can do it without enabling publishing feature. Most of the examples on the web uses publishing feature.

Create a new document library with ‘web part page’ document template

The following code snippet shows how to Create the web part page document library.

private void CreateWebPartPageLibrary(SPWeb web)
{
//get the document library list template
var documentListTemplate = web.ListTemplates["Document Library"];
//get document template (word file,excel file, web part pages etc) that will be used in the documentlibrary
var webPartPageDocTempalte = GetWebPartPageDocTemplate(web);
web.Lists.Add(PAGE_LIBRARY_NAME, "Programmatically added library", documentListTemplate, webPartPageDocTempalte);

}
private SPDocTemplate GetWebPartPageDocTemplate(SPWeb web)
{
SPDocTemplate docTemplate = null;
foreach (SPDocTemplate template in web.DocTemplates)
{
if (template.Name.Equals("Web Part Page", StringComparison.OrdinalIgnoreCase))
{
docTemplate = template;
break;
}
}
return docTemplate;
}

In the CreateWebPartPageLibrary method, the first line get the template for document library. Then the GetWebPartPageDocTemplate return the document template for “web part page”. Finally I have called web.lists.add method to add our new list to the list collection.

 

Add a web part page in Web Part page library

Once you have web part page library, you need to add web part page in the library. You can do so programmatically. To add the page you need to prepare an xml command and execute with with ProcessBatchData method.

SPList list = web.Lists["MyPagesLibrary"];
string postInformation =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<Method>" +
"<SetList Scope=\"Request\">" + list.ID + "</SetList>" +
"<SetVar Name=\"ID\">New</SetVar>" +
"<SetVar Name=\"Cmd\">NewWebPage</SetVar>" +
"<SetVar Name=\"Type\">WebPartPage</SetVar>" +
"<SetVar Name=\"WebPartPageTemplate\">1</SetVar>" +
"<SetVar Name=\"Title\">{0}</SetVar>"+
"<SetVar Name=\"Overwrite\">true</SetVar>" +
"</Method>";

web.ProcessBatchData(postInformation);

In the above code snippet a command is prepared to generate a web part page. In the command the Type is WebPartPage. WebPartPageTemplate is for different page layout that we can find in the sharepoint UI. The page title is only title without aspx.

Add web part to webpart page

You have created a ‘web part page’ library and added a webpart page. Now you need to add your web part in that page. First of all you need to create a web part instance. The following code Snippet shows how to create a web part programmatically.

private System.Web.UI.WebControls.WebParts.WebPart GetWebPart(SPWeb web, string webPartName)
{
var query = new SPQuery();
query.Query = String.Format(
"<Where><Eq><FieldRef Name='FileLeafRef'/><Value Type='File'>{0}</Value></Eq></Where>",
webPartName);

SPList webPartGallery;
if (web.IsRootWeb)
{
webPartGallery = web.GetCatalog(
SPListTemplateType.WebPartCatalog);
}
else
{
webPartGallery = web.ParentWeb.GetCatalog(
SPListTemplateType.WebPartCatalog);
}
var webParts = webPartGallery.GetItems(query);
var typeName = webParts[0].GetFormattedValue("WebPartTypeName");
var assemblyName = webParts[0].GetFormattedValue("WebPartAssembly");
var webPartHandle = Activator.CreateInstance(
assemblyName, typeName);

System.Web.UI.WebControls.WebParts.WebPart webPart =
(System.Web.UI.WebControls.WebParts.WebPart)webPartHandle.Unwrap();
return webPart;
}

The web part name in the above method GetWebPart() is like “employeeviewer.webpart”. The webpart collection exists in the rootweb. So I have check the IsRootWeb to make sure I run the caml query against the root web.

Once you have got the web part instance created by Activator.CreateInstance you can add it in the page.

const string WEBPART_TITLE="test web part"
//get the web part with GetWebPart method that I have posted in this blog
System.Web.UI.WebControls.WebParts.WebPart webPart = GetWebPart(web, "wpCopyFile.webpart");
using (webPart)
{
using (SPLimitedWebPartManager manager = web.GetLimitedWebPartManager(string.Format("Pages/{0}.aspx",WEBPART_PAGE_NAME), PersonalizationScope.Shared))
{
//if web part already exists then return
foreach (System.Web.UI.WebControls.WebParts.WebPart oldWebPart in manager.WebParts)
{
if (oldWebPart.Title.Equals(WEBPART_TITLE))
return;
}
webPart.Title = WEBPART_TITLE;
manager.AddWebPart(webPart, "", 0);
manager.SaveChanges(webPart);
}
}


The code snippet above call the GetWebPart() method to get the instance of the web part. Then using LimitedWebPartManager it get the page’s web part manager. Then it check if the web par already exists in the page by checking title. And finally add the web part if it doesn’t exists. FYI, be careful to dispose webpart and SPLimtiedWebPartManager.

Monday, February 8, 2010

SharePoint 2010: Client Object Model – an Introduction

SharePoint 2007 allows using its Object model to run against server running SharePoint. For clients (not running SharePoint in the box) the simplest way to communicate with SharePoint server is web services. SharePoint Client Object Model (OM) can be run on client PC (where SharePoint is not installed) to communicate with SharePoint server. So whereas SharePoint (Server) Object Model runs in a SharePoint server and can manipulate SharePoint objects, Client OM can run in client PC and communicate with SharePoint server remotely.

SharePoint 2010 introduces three new client APIs which can be used to interact with SharePoint sites. The three APIs are targeted for three different types of clients:

1. For .net Managed applications (for example, console applications, window applications, web applications etc, which are not running inside SharePoint Context).

2. For Silverlight applications.

3. For using with JavaScript (called ECMAScript). This API is only available for applications hosted inside SharePoint (for example, web part deployed in SharePoint site can use this JavaScript API for accessing SharePoint from browser using JavaScript).

I’ll explain all of these API sets gradually. In this post I’ll explain some basic problems SharePoint developers faced during development with SharePoint 2007 in absence of Client Object Model.

Why Client Object Model (OM)?

SharePoint 2007 had no Client Object model available. So you may ask why this is introduced in SharePoint 2010? We had no problem without Client OM and millions of sites are running smoothly without having Client OM. The main reason is that Microsoft has found lot of requests from SharePoint users to introduce more and more web services to get data out of SharePoint in the last couple of years. But introducing web services will not fix the issues, as Microsoft found, because then the request for more functionality in the web services will continue. Even if Microsoft provides a good numbers of web services with SharePoint, customization in web services will be required for different clients and this will make the out of the box web services unusable. Also introducing a large number of web services will be a waste as not all companies will use all the web services functionalities.

In response to add more web services from users, Microsoft has taken a different approach called Client Object Model (OM). This SharePoint Client OM will allow getting data out of SharePoint from PCs that are not hosting SharePoint. Also Client OM provides complete API to interact with SharePoint Server which is more intuitive and useful and very much similar with SharePoint Object Model.

Similarity with SharePoint Object Model

Now SharePoint developers will fear that the Client Object Model will introduce new burden for them to get used to it. But SharePoint team provided great efforts to keep the Client OM familiar with SharePoint Object Model. The following table shows the equivalent objects in Client and SharePoint Object Model.

Server (Microsoft.SharePoint)

Client Object Model

SPContext

ClientContext

SPSite

Site

SPWeb

Web

SPList

List

SPListItem

ListItem

SPField

Field

So the class names in Client OM are similar as like SharePoint Object Model. However the way client OM will be used a bit different than usual SharePoint Object Model that we will explore in the upcoming posts.

How Client OM is developed and work under the hood?

It’s interesting how SharePoint team has developed the same set of classes for three different sets of applications (Managed, Silverlight and ECMAScript). There is same class ListItem for three different set of applications. As shown in the following table, three different assemblies/files are used for three different types of applications.

Client Type

Assembly/File

Managed Client

Microsoft.SharePoint.Client

Silverlight

Microsoft.SharePoint.Client.Silverlight

ECMAScript

SP.js

To ensure the same class object (say ListItem) behaves similarly in three different types of applications SharePoint team followed the steps described below:

a) SharePoint team first set attributes to the SharePoint classes and methods and properties that need to be exposed in Client OM.

b) Then a code generator is run against the SharePoint object model to generate client OM automatically.

This automated code generation has ensured maximum compatibility between these three sets of APIs. As the following figure shows client communicate to the server thorough Client OM which under the hood uses Client.svc WCF service to communicate with SharePoint Server. Client.svc service uses Server OM as per client request and return result to the client in JSON format.

image

Figure: How Client Object model works with Server

With this new Client OM, we almost don’t need to use web service to communicate with SharePoint server. However, the client OM just released and we will find its shortcomings as well use it more and more in live projects. In my next posts I’ll go through three differents kinds of Client OM (Managed .net, Silverlight and ECMAScript).

Thursday, February 4, 2010

SharePoint 2007: Hide/Show options from Create page.

When you navigate to Site Settings –> create page you’ll be directed to the create.aspx page as shown below:

image

Figure 1: create page

Now if you want to hide some options in the create page, then how will you do that? Say you don’t want to show the “Document Library” option or you want to hide this option for a particular user or group.

 

Hide option for all users

Say you want to hide “Document Library” option for all users. The following steps will guide you how to do so. But before you proceed with the steps please backup your original create.aspx page.

1. Locate the create.aspx page on your disk: If you take a look at the url of the create page you’ll find it something like “http://server:port/_layouts/create.aspx”. So the create page is from layout folder which is from “Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\layouts”. You can open the Create.aspx page in Visual Studio. In the page you’ll find a code block as shown below:

System.Collections.IEnumerator currRg = ((ArrayList)rgRgs[iCat + 5]).GetEnumerator();
while (currRg.MoveNext())
{
                SPListTemplate spListTmpl = (SPListTemplate)currRg.Current;
    SPListTemplateType iTemplateType = spListTmpl.Type;
    if (iTemplateType == SPListTemplateType.InvalidType)
        continue;
 ......................................
}

The above code block actually add create options in the page. So if you modify the code block you can filter the options to show on the page.

 

2. Find the feature id of the create option you want to hide: In order to hide the option you need to know the feature id associated with this create option. To do so move your mouse over the “Document Library” and you’ll find the feature id as shown below:

image

Figure 2: Find feature ID from create page

3. Modify the Create.aspx page to hide option: From step 2 we have found the feature id (of Document Library). Now we need to modify the code in the page to hide the option.

while (currRg.MoveNext())
{
    SPListTemplate spListTmpl = (SPListTemplate)currRg.Current;
    SPListTemplateType iTemplateType = spListTmpl.Type;
    if (iTemplateType == SPListTemplateType.InvalidType)
    continue;
    Guid featidTemplate = spListTmpl.FeatureId;
                
                
    //Added for hiding the "Document Library" option
    if (featidTemplate.ToString() == "00bfea71-e717-4e80-aa17-d0c71b360101")
    continue;
                
                
                
    string strTemplateDisplayName = SPHttpUtility.HtmlEncode(spListTmpl.Name);
    string strTemplateDisplayNameScript = SPHttpUtility.EcmaScriptStringLiteralEncode(spListTmpl.Name);

................................
}

In the above code snippet I have added a condition

if (featidTemplate.ToString() == "00bfea71-e717-4e80-aa17-d0c71b360101") continue;

to check if the featuretemplate id the one for Document Library. If so then I’m continuing to the next iteration rather than adding the option to the page.

 

Hide Options based on User/group

We can hide/show option based on user or group. To check user group we can use SPContext. If you add the following code snippet in place then it’ll show the document library option only for Admins.

if ((featidTemplate.ToString() == "00bfea71-e717-4e80-aa17-d0c71b360101")&&
(!SPContext.Current.Web.CurrentUser.IsSiteAdmin))
continue;

Make sure the hide/show change doesn’t affect other sites

If you make changes to the original create.aspx page then all the sites in that web serve will be affected as the file is shared across all sites. To make sure that only your site gets affected for the changes you need to follow the following steps:

1. Copy the orignal _layouts folder (C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\layouts) to some other folder (say C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\80_layouts).

2. Go to IIS and expand the site and navigate to the _layouts tree node. Then open the _layouts properties window and change the local path to the place where you have copied folder on step 1(in my case 80_layouts). This is shown below:

image

Figure 3: Change the _layouts virtual directory location

3. Now you can modify the create.aspx page following the steps described on step “hide option for all users” or “hide options based on user/group”.

Monday, January 25, 2010

SharePoint 2010 Beta Installation Error: An exception of type Microsoft.Office.Server.UserProfiles.UserProfileException was thrown

I was installing SharePoint 2010 beta in Windows server 2008 x64. Everything went fine till installation and when I run configuration wizard it thrown the following exception at step 8:

An exception of type Microsoft.Office.Server.UserProfiles.UserProfileException was thrown.  Additional exception information: The request channel timed out while waiting for a reply after 00:00:19.9589870. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout.”

Then I run the central administration site and it was working fine. But when I tried to run default site (at port 80), it was breaking. So I tried to find out the issue and finally found that it’s an timeout issue. The problem is that the user profiling WCF service was timed out. To fix this I increased the timeout value in the file “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\WebClients\Profile\client.config”. The modification was in the following lines.

<binding name="ProfileServiceHttpsBinding"
            receiveTimeout="00:00:20"
            sendTimeout="00:00:20"
            openTimeout="00:00:20"
            closeTimeout="00:00:20">

In the above code snippet the timeout value is 20 sec. You can change it some other values (more than 20 secs). I had put 3 minutes as shown below:

<binding name="ProfileServiceHttpsBinding"
            receiveTimeout="00:03:00"
            sendTimeout="00:03:00"
            openTimeout="00:03:00"
            closeTimeout="00:03:00">

After changing the timeout value I had run the configuration wizard again and found the issue resolved.