Saturday, March 30, 2013

Camlex.Net 3.4 and Camlex.Client 1.2 are released

Today I’ve released new versions of the Camlex.Net open source project: Camlex.Net 3.4 (basic Camlex for Sharepoint object model) and Camlex.Client 1.2 (Camlex for client object model). Both libraries are also available in NuGet gallery. In order to add them to your project execute the following commands in VS Package manager:

Install-Package Camlex.NET.dll

Install-Package Camlex.Client.dll

Interesting that it was the 1st release which contains pull request from another user’s fork. In both branches the following new features were added:

1. Join several string conditions into single query

Now it is possible to pass array of string conditions for CAML query. Camlex will join them and will build correct CAML tree. This feature will be useful if you have some other tools or libraries which work with string queries, and would like to simplify joining of these conditions. For example if we have 3 following conditions:

   1: var expr = new List<string>();
   2: expr.Add(
   3:     "<Where>" +
   4:     "   <Eq>" +
   5:     "       <FieldRef Name=\"Field1\" />" +
   6:     "       <Value Type=\"Integer\">1</Value>" +
   7:     "   </Eq>" +
   8:     "</Where>");
   9: expr.Add(
  10:     "<Where>" +
  11:     "   <Eq>" +
  12:     "       <FieldRef Name=\"Field2\" />" +
  13:     "       <Value Type=\"Integer\">2</Value>" +
  14:     "   </Eq>" +
  15:     "</Where>");
  16: expr.Add(
  17:     "<Where>" +
  18:     "   <Eq>" +
  19:     "       <FieldRef Name=\"Field3\" />" +
  20:     "       <Value Type=\"Integer\">3</Value>" +
  21:     "   </Eq>" +
  22:     "</Where>");

and want to create common query which will return all items which have all conditions met, i.e. Field1 = 1 AND Field2 = 2 AND Field3 = 3, then we can write:

   1: string caml = Camlex.Query().WhereAll(expr).ToString();

and it will produce the common query:

   1: <Where>
   2:   <And>
   3:     <And>
   4:       <Eq>
   5:         <FieldRef Name="Field1" />
   6:         <Value Type="Integer">1</Value>
   7:       </Eq>
   8:       <Eq>
   9:         <FieldRef Name="Field2" />
  10:         <Value Type="Integer">2</Value>
  11:       </Eq>
  12:     </And>
  13:     <Eq>
  14:       <FieldRef Name="Field3" />
  15:       <Value Type="Integer">3</Value>
  16:     </Eq>
  17:   </And>
  18: </Where>

As you can see Camlex created correct result conditions tree automatically. By the same way you may create query which will return items which have at least one condition met, using WhereAny method (it will use Or instead of And).

2. Support for native double type

Prior to 3.4 version (and 1.2 client version) in order to create queries with Number data type, you needed to use string-based syntax:

   1: string caml = Camlex.Query().Where(x => x["Foo"] ==
   2:     (DataTypes.Number)"1.2").ToString();

which will produce the following CAML:

   1: <Where>
   2:   <Eq>
   3:     <FieldRef Name="Foo" />
   4:     <Value Type="Number">1.2</Value>
   5:   </Eq>
   6: </Where>

It is called string-based, because rvalue is represented by string, which is casted to the CAML datatype (DataTypes.Number in the example above). We introduced string-based syntax, because there is no 1 to 1 mapping between C# types and CAML types (e.g. User or WorkflowStatus CAML types). But there are several types which exist both in CAML and in C#: int and Integer, string and Text, DateTime and DateTime. Now double C# type is mapped to Number CAML type. So it is possible to rewrite the above query using shorter version:

   1: string caml = Camlex.Query().Where(x => (double)x["Foo"] == 1.2).ToString();

As you can see now rvalue is represented by real C# double which is more simpler and intuitive.

This change is also first change which was added from fork of another user (not Camlex developer) with nickname Shapel who sent pull request to Camlex using Codeplex functionality. So I would like to thank Shapel for this contribution and remind that Camlex is free open source project which is open for your contribution. So please fell free to create your forks and send pull requests. It will help to make Camlex more valuable for our every day work.

Managed metadata navigation and friendly URLs in Sharepoint 2013 – part 1

Managed metadata navigation and friendly URLs is one of my favorite features in Sharepoint 2013. Finally we may get rid of /pages/default.aspx and live with SEO (remember that Google uses URLs in the ranking) and user-friendly URLs for pages and sub sites. This feature allows also to show content from different locations inside one site in Sharepoint, so end users even won’t actually know that it actual content is stored somewhere else. In this part I will make overview of basic functionality and in the second part will describe more complicated features (Update 2013-04-21: second part is here and third part is here).

Let’s see how it works. First of all you need to enable managed metadata driven navigation in Site settings > Navigation:

image

After that you will be able to manage navigation nodes in the local site collection’s managed metadata group. It can be found in Site settings > Term store management:

image

Note that each site collection has own local managed metadata group, which is shown only for its own site collection, i.e. when you open Term store management in context of its site collection. E.g. local groups are not shown when you open term store in Central administration > Managed service applications > Managed metadata service. You can use predefined Site Navigation term set or create your own. In the last case you will need to change default navigation term set in Site settings > Navigation:

image

Let’s add several terms into term set and check how navigation will look like. If we have the following terms:

image

then global and current navigation will look like this:

image

As you can see it reflects the hierarchy of the terms which we created in the term store. Note “Edit links” link near both navigations. This is very useful tool from my point of view which allows to modify navigation in WYSIWYG mode:

image

In the example above I’ve added Investors link which points to external http://example.com URL, i.e. links are not limited by the local Sharepoint URLs. If you will check navigation term set after that, you will see that Investors link is added on appropriate level, i.e. Sharepoint automatically synchronizes changes made in the navigation editor and term set:

image

For current navigation 2nd higher level links by default are shown in the dynamic menu. It is also possible to edit these nodes using WYSIWYG editor:

image

Also you may hide (see eye icon near each node), rearrange by using drag and drop, add new and modify existing nodes – and all this can be done without leaving the public view of the site. The same actions can be done in Site settings > Term store management, but it will require knowledge of the term store functionality for content producers. Using WYSIWYG editor it is more simpler and intuitive.

This was basic overview of managed metadata navigation for Sharepoint 2013. In next part I will write about more advanced features and will describe another details of its behavior.

Thursday, March 28, 2013

Drawbacks of using search-driven approach in Sharepoint 2013

On the SPC 2012 where Sharepoint 2013 was officially presented to the public (although it was release a few weeks before) one of the raised question was: why to not use search-driven approach? The idea of this approach is quite simple: there is authoring site where content owners create content. This site is not available for regular visitors. Content from authoring site is crawled by search crawler and stored in the search index. Also there is publishing site, where content producers define how content from authoring site should look like. They use new standard Content by search web parts, Catalogs, Cross site publishing, Catalog item reuse web parts for that. In the web parts designers define display templates for the content. This concept is shown on the following picture from technet:

image

In conjunction with the new continuous crawl feature of the Sharepoint search, which allows to decrease the gap between publishing of the content and the moment when it become available in the search index (and those shown to end users), this technique is really quite powerful. It also gives performance advantages comparing with Content by query web parts: in the last case queries to content database are performed synchronously when user loads the page with web part, which consumes server resources, while in search-driven approach queries are done to the search index which contains crawled content already (although queries to the search index is also not a panacea. In Sharepoint 2010 queries by all fields except standard rank property could be very slow, because on the search database level index was built only for the rank property. I need to verify how it will work with search in Sharepoint 2013). Crawling itself is often performed on separate application server with dedicated WFE, which is not used by the load balancer, i.e. which doesn’t process user requests.

Advantages are good, but are there any problems with this approach? At the current moment we already have experience of using Sharepoint 2013 in the real projects and I may share some information about drawbacks of search-driven approach.

The first classical problem is delay, because even with continuous crawl there is no guarantee that data will be crawled immediately. For some business cases it is crucial requirements, e.g. when company publishes annual report on the public site and it should become available exactly at the same time when it is published in other media.

The second problem affects general content creation model. With search-driven approach content owners can’t use web parts on the authoring site. Or to be more precise: they can use it, but content inside them won’t be shown on the publishing site. Crawler will crawl content fields of the pages on the authoring site and these fields will appear in crawled properties list, which then can be mapped to managed properties. On the publishing site Content by search web parts will contain queries which will use these properties in order to display content. So unless you show whole page as plain text, you won’t be able to display web parts content on the publishing site.

As you probably know starting with Sharepoint 2010 it is possible to add web parts not only to web part zones, but also to the rich html fields of the publishing pages. So may be this would be solution? Unfortunately no: we tested this scenario and all web parts which were added to the content field were lost in the search index.

Problem with web parts can be very significant for the users who worked with Sharepoint 2007 and 2010 before, because it means that they can’t use classic working model with Sharepoint, where it is possible to create pages and put web parts on them. Instead they should use fields controls and put content inside them. In turn it means lose of WYSIWYG. Although it is possible to create page layouts with field controls defined by the same way as they will be displayed on the publishing site, but in this case you will need to have predefined lists of page layouts on the authoring site and publishing sites which will correspond to each other (e.g. on authoring site page layouts should use field controls, while on publishing site page layouts may still use web part zones).

There is also another problem related with displaying correct targeted content in the site search when search-driven approach is used with cross-site publishing and managed metadata navigation. But at the moment we are still looking for the solution of this problem and doing more experiments. Once I will have more details about this problem I will update this post.

Hope that knowing drawbacks of the search-driven approach will allow you to make correct decisions. If you faced with another problems, please share them in comments.

Saturday, March 2, 2013

Fix problem Failed to get value of the "Enterprise Keywords" column in Sharepoint

In this post I will describe how to fix the following error:

Exception message: Failed to get value of the "Enterprise Keywords" column from the "Managed Metadata" field type control. See details in log. Exception message: Invalid field name. {1390a86a-23da-45f0-8efe-ef36edadfb39}

Microsoft.SharePoint.WebControls.BaseFieldControl.OnLoad(EventArgs e)
Microsoft.SharePoint.Taxonomy.TaxonomyFieldControl.OnLoad(EventArgs e)
System.Web.UI.Control.LoadRecursive()

System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

You may face with it e.g. when edit publishing page. It says that there is no field with Id = {1390a86a-23da-45f0-8efe-ef36edadfb39} in the underlying content type. This field is hidden Note field TaxKeywordTaxHTField which is supplementary field for OTB Enterprise Keywords managed metadata field (Id = {23f27201-bee3-471e-b2e7-b64fd8b7ca38}). There are 2 ways which you can use for fixing the problem:

1. Check the underlying content type of the page which you create/edit. It should contain both fields: TaxKeywordTaxHTField and TaxKeyword. If it contains only TaxKeyword, you will encounter with the problem above. I wrote about this solution here: Add Enterprise keywords field into custom content type.

2. Go to the doclib settings (Pages > Settings) and go to Permissions and Management > Enterprise Metadata and Keywords Settings:

image

And then ensure that checkbox “Add an Enterprise Keywords column to this list and enable Keyword synchronization” is checked:

image

Once you enable it, it won’t be possible to disable it from UI (checkbox will be checked and disabled).

Also it is possible to enable it programmatically. If you will check codebehind of the application layouts page “Enterprise Metadata and Keywords Settings” (metadatacolsettings.aspx), you will find that it uses internal class MetadataListFieldSettings. In order to enable it programmatically we have to use reflection:

   1: var assembly = Assembly.LoadWithPartialName("Microsoft.SharePoint.Taxonomy");
   2:  
   3: var type = taxonomy.GetType("Microsoft.SharePoint.Taxonomy.MetadataListFieldSettings");
   4: object settings = type.GetConstructor(new Type[] { typeof(SPList) })
   5: .Invoke(new object[] { list });
   6: type.GetProperty("EnableKeywordsField", BindingFlags.NonPublic | BindingFlags.Instance)
   7: .SetValue(settings, true, null);
   8: type.GetProperty("EnableMetadataPromotion", BindingFlags.NonPublic | BindingFlags.Instance)
   9: .SetValue(settings, true, null);
  10: type.GetMethod("Update", BindingFlags.NonPublic | BindingFlags.Instance)
  11: .Invoke(settings, null);
  12:  
  13: type.GetMethod("KeywordsFieldExistsInContentTypes", BindingFlags.NonPublic | BindingFlags.Instance)
  14: .Invoke(settings, new object[] { true });
  15: type.GetMethod("Update", BindingFlags.NonPublic | BindingFlags.Instance)
  16: .Invoke(settings, null);

After performing one of these two steps, error should disappear.