Karine Bosch’s Blog

On SharePoint

New release of CAML Designer available with new functionality!

042813_0652_Newversiono1.png

This weekend we released a new version of the CAML Designer.We solved the bug that occurred when more than one TaxonomyField was selected in the Where clause; but we also include the following new functionality:

- The <Membership> element

- REST snippets including a CAML query

We are very proud with these new additions, and we hope you like it!

 

 The <Membership> element

The <Membership> element already exists for ages but it’s only now that we introduce it in the CAML Designer.

Whenever you select a field of user type in the Where clause, you will now get an additional radio button “Membership”:

User field

When you click the Membership option, you get an additional dropdown where you can select one of the following options:

  • CurrentUserGroups: this will retrieve all tasks that are assigned to groups to which the current user belongs.
  • SPWeb.Groups: this will retrieve all tasks that are assigned to groups.
  • SPWeb.AllUsers: this will retrieve all tasks that have been assigned to users and not to groups.
  • SPWeb.Users: this will retrieve all tasks that have been assigned to users that don’t belong to a specific group but that have been granted access to the site directly.
  • SPGroup: this will retrieve tasks that have been assigned to a specific group.

User field - membership

We’ve also foreseen a small description field that explains the selected option:

User field - membership CurrentUserGroups

When selecting the SPGroup option, another additional dropdown becomes available. This dropdown will list all groups:

User field - membership SPGroup

Selecting a group from this dropdown will retrieve all tasks that have been assigned to the selected group. The CAML query looks like the following:

   <Where>
      <Membership Type='SPGroup' ID='5'>
         <FieldRef Name='AssignedTo' />
      </Membership>
   </Where>

When you select the CurrentUserGroups option, the CAML query will look as follows:

  <Where>
      <Membership Type='CurrentUserGroups'>
         <FieldRef Name='AssignedTo' />
      </Membership>
   </Where>

For the SPWeb.Groups option, the CAML query looks like this:

  <Where>
      <Membership Type='SPWeb.Groups'>
         <FieldRef Name='AssignedTo' />
      </Membership>
   </Where>

For the SPWeb.AllUsers options, the following CAML query is generated:

  <Where>
      <Membership Type='SPWeb.AllUsers'>
         <FieldRef Name='AssignedTo' />
      </Membership>
   </Where>

And when you select the SPWeb.Users option, you will get the following CAML:

  <Where>
      <Membership Type='SPWeb.Users'>
         <FieldRef Name='AssignedTo' />
      </Membership>
   </Where>

Credits go to Christoper Clementen with his blog post: http://christopherclementen.wordpress.com/2012/08/27/caml-query-membership-attribute where he clearly explains how the <Membership> element works.

 

REST snippets executing CAML queries

A second new functionality are the REST snippets with CAML queries. In most cases you can use the well known REST requests using the select and filter criteria to retrieve list items. But there are a few cases where such a REST request will return an error. These cases are:

  • filtering on taxonomy fields
  • the <Membership> element

In these cases your REST snippet must include a CAML query. Additionally this REST request needs to be executed as a POST. This is well described in this blog post of CleverWorkarounds.

Such a REST snippet generated by the CAML Designer looks like this:

 

$.ajax({ 
   url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Developers')/GetItems(query=@v1)?"
        + "@v1={\"ViewXml\":\"<View><ViewFields><FieldRef Name='Title' /><FieldRef Name='FirstName' /><FieldRef Name='Company' /></ViewFields>"
        + "<Query><Where><Eq><FieldRef Name='Technology' /><Value Type='TaxonomyFieldType'>SharePoint</Value></Eq></Where></Query></View>\"}", 
   type: "POST", 
   headers: { 
         "X-RequestDigest": $("#__REQUESTDIGEST").val(), 
         "Accept": "application/json;odata=verbose", 
         "Content-Type": "application/json; odata=verbose" 
   }, 
   success: function (data) { 
      if (data.d.results) { 
         // TODO: handle the data  
         alert('handle the data'); 
      } 
   }, 
   error: function (xhr) { 
      alert(xhr.status + ': ' + xhr.statusText); 
   } 
});

When filtering on membership, the REST snippet will look like this:

$.ajax({ 
   url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Tasks')/GetItems(query=@v1)?@v1={\"ViewXml\":\"<View><Query><Where><And><Neq><FieldRef Name='Status' /><Value Type='Choice'>Completed</Value></Neq><Membership Type='SPWeb.Groups'><FieldRef Name='AssignedTo' /></Membership></And></Where></Query></View>\"}", 
   type: "POST", 
   headers: { 
         "X-RequestDigest": $("#__REQUESTDIGEST").val(), 
         "Accept": "application/json;odata=verbose", 
         "Content-Type": "application/json; odata=verbose" 
   }, 
   success: function (data) { 
      if (data.d.results) { 
         // TODO: handle the data  
         alert('handle the data'); 
      } 
   }, 
   error: function (xhr) { 
      alert(xhr.status + ': ' + xhr.statusText); 
   } 
}); 

May 22, 2014 Posted by | CAML Designer, SharePoint 2013 | 2 Comments

Where are the SharePoint assemblies?

In SharePoint 2013, the SharePoint assemblies are located in the \Windows\Microsoft.NET\assembly\GAC_MSIL\ directory:

SharePoint 2013 assemblies

All other SharePoint files are located in what we call the 15 hive: \Program Files\Common Files\Microsoft Shared\Web Server Extensions\15.

When inspecting IIS, you will notice a change in the virtual directories: the _layouts folder still points to the 14 hive (containing the SharePoint 2010 files), while there is an additional sub folder that points to the 15 hive (containing the SharePoint 2013 files).

SharePoint virtual folders

 

When navigating through the file system, you can see that there is still a 14 hive, containing folders like TEMPLATE, TEMPLATE\CONTROLTEMPLATES and TEMPLATE\LAYOUTS. When you edit one of the controls in the CONTROLTEMPLATES folder, you will notice that they still reference the old SharePoint v14 assemblies. If you deploy your SharePoint 2010 solutions and features to your SharePoint 2013 farm, it is in this 14 hive that you will find your files.

But there is no ISAPI folder anymore in the 14 hive, nor are the assemblies in the Global Assembly Cache. Calls to the old assemblies are redirected to the SharePoint v15 assemblies. This redirection is defined in special policy files located in the \Windows\Microsoft.NET\assembly\GAC_MSIL\ directory:

SharePoint policy files

 

April 19, 2014 Posted by | SharePoint 2013 | Leave a comment

SharePoint Saturday

SPS 2012 Belgium

SharePoint Saturday 2014 is coming near and we all start to feel the vibes. April 26th is the big day! We just made the session agenda available online. Check it out here!

April 8, 2014 Posted by | SharePoint 2010 | Leave a comment

New release of CAML Designer available!

042813_0652_Newversiono1.png

My friend Andy Van Steenbergen and I worked very hard to prepare a new release of the CAML Designer. We didn’t aim at adding new functionality; we rather preferred to solve a number of bugs. You can download the new version on the BIWUG site under the Download tab.

We also have the impression that the server object model works much slower than the client object model. Therefore we switched the default work mode to client object model.

There are also a number of bugs that were reported but that are not included in this release:

  • The <Membership> element is missing
  • For complex queries, the CAML query can be included in the REST call
  • Taxonomy fields: only top-level of multi-select field was visible

The reason why these bugs are not solved yet, is because these bugs are not that simple to solve and we didn’t want to wait much longer with this release because of the fixes that are already in it. We will try to solve the remaining bugs as soon as possible.

We hope you enjoy the improvements. If you would encounter bugs or have a good idea on how we could further improve the CAML Designer, please don’t hesitate to report it to camlfeedback@biwug.be  or leave a note on this blog. Together we can make it a better tool.

Show hidden fields

In some cases you want to build a filter based on a value in a hidden field. By default the CAML Designer doesn’t show hidden fields. To solve this issue we added the check box “Show hidden fields” to the user interface just above the list treeview. By default the hidden fields are not displayed, but you can click this check box to get them displayed.

Show hidden fields

Long Display Names

One of our users als reported that there were issues with long display names. It would ask a lot of redesign to have this properly displayed so we chose to add a tooltip that shows the complete display name. We hope that this suits your needs.

Long display names

Boolean fields

There was also a problem with querying boolean fields. This issue is now solved:

  <Where>
      <Eq>
         <FieldRef Name='VeryTall' />
         <Value Type='Boolean'>1</Value>
      </Eq>
   </Where>

I tested and retested, and this query returns rows:

Boolean field query result

User and UserMulti fields

There were a number of problems when filtering on a User field or UserMulti field. These have been solved. Now, you can choose between filtering on the current user or on a specific user.

User field - current user

If you choose to filter on the current user, your query will look as follows:

   <Where>
      <Eq>
         <FieldRef Name='AssignedTo' />
         <Value Type='Integer'>
            <UserID />
         </Value>
      </Eq>
   </Where>

If you choose to filter on a specific user then you can enter or the name of the user or its ID. If you enter the name of a user, your query looks as follows.

   <Where>
      <Eq>
         <FieldRef Name='AssignedTo' />
         <Value Type='User'>Karine Bosch</Value>
      </Eq>
   </Where>

If, at the other side, you want to filter on the user ID, the following query is generated, indicating that you are passing an ID:

   <Where>
      <Eq>
         <FieldRef Name='AssignedTo' LookupId='True' />
         <Value Type='Integer'>4</Value>
      </Eq>
   </Where>

The FileRef field

There was also a problem when trying to filter on FileRef. When you now select the FileRef field on the Where tab, you get a lookup displaying all folders in the selected document library.

FileRef field

When you select a folder, the query is built as follows:

   <Where>
      <Eq>
         <FieldRef Name='FileRef' />
         <Value Type='Lookup'>/Shared Documents/Folder 2</Value>
      </Eq>
   </Where>

REST snippet for lookup fields

Based on a blog post of Andrew Connell “Applying Filters to Lookup Fields with the SharePoint 2013 REST API“, we also updated how the REST snippet for lookup fields is generated:

$.ajax({
   url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Cities')/Items?$expand=Country/Title&$filter=Country/Title eq 'France'",
   type: "GET",
   headers: {"accept": "application/json;odata=verbose"},  
   success: onDataReturned; 
   error: onError; 
   function onDataReturned(data){ // TODO: handle the data }; 
   function onError(err) { // TODO: handle the error }
});

Taxonomy Fields

We also added functionality to retrieve list data based on the ID of a taxonomy field. The difficulty here is that a term has a name and an guid in the term store. You can retrieve list data based on a term name but this is dangereous as term names can change over time.

Taxonomy Field

   <Where>
      <Eq>
         <FieldRef Name='Technology' />
         <Value Type='TaxonomyFieldType'>SharePoint</Value>
      </Eq>
   </Where>

When a term is used to tag a list item, this term is stored in a hidden list on site collection level. At this time, the term gets an ID, which is stored internally as WssId.

This release of the CAML Designer now offers the possibility to query on this ID:

Taxonomy Field by ID

The query instantly changes as follows:

   <Where>
      <Eq>
         <FieldRef Name='Technology' LookupId='True' />
         <Value Type='Integer'>1</Value>
      </Eq>
   </Where>

It will retrieve the same list items as when you would have queried on the term “SharePoint”.

The Test tab

The Test tab displays now a label that indicates the number of rows that are returned by the result set. Once the number becomes visible, you know that the retrieval has finished. This is handy when your query does not return a result and no grid becomes visible. The number zero will indicate that the query is finished but that no results have been returned.

March 17, 2014 Posted by | CAML Designer, SharePoint 2013 | 3 Comments

SharePoint Saturday Belgium

SPS 2012 Belgium

On Saturday April 26th we are having our 4th SharePoint Saturday. When it was officially announced to our members, it only took 48 hours to be sold out. Today we officially communicate the sessions that are elected. As always we try to have a right mix of sessions for developers, IT Pro and business people. We also target a variety of domains of SharePoint 2013, like Apps, Search, Workflows and BI. As in the previous years, we also have a good mix of foreign speakers and local speakers. Some of them are coming back for the 3th time.

If you’re curious, browse right away to the speakers page on the SPS site to get a detailed overview of all sessions! I hope you will be as excited as we are!

Hope to see you there!

 

March 12, 2014 Posted by | BIWUG | Leave a comment

SharePoint 2013 SP1 is available for download now!

The release of SP1 for SharePoint 2013 has been announced on February 25th. It is available for download from the Microsoft Download Center.

My colleague Tom Van Gaever did a nice job in listing all issues that have been solved in SP1.

You can find the official documentation here.

 

March 5, 2014 Posted by | SharePoint 2013 | Leave a comment

SharePoint Saturday 2014 is coming soon!

SPS 2012 Belgium

The BIWUG board is proud to announce a new edition of SharePoint Saturday Belgium!

The event takes place on Saturday – of course :) – April 26th at the same location as the previous years: Xylos, St-Lendriksborre 8, 1120 Brussels.

The call for speakers will open today and of course the subscriptions too! Keep an eye on twitter and the BIWUG site if you want to be amongst the first to subscribe.

January 20, 2014 Posted by | BIWUG | Leave a comment

Declaring a taxonomy field that allows multiple values

There are two different types of taxonomy fields:

  • TaxonomyFieldType: This defines a taxonomy field to store a term to tag the list item.
  • TaxonomyFieldTypeMulti: This defines a taxonomy field that allows users to tag the list item with more than one term from the same term set.

When using CAML, you can declare taxonomy fields as follows:

  <Field  ID="{CB736B25-C980-4688-B076-1C5315B20092}" 
          Name="ProductCategory" DisplayName="Product Category" 
          Type="TaxonomyFieldType"
          ShowField="Term1033" 
          EnforceUniqueValues="FALSE" 
          Group="XYZ Site Columns" 
          SourceID="http://www.xyz.be/v1" />

  <Field  ID="{6E8C115D-43DE-4AD9-9DE4-27F5D4C008F4}" 
          Name="ProductCategories" DisplayName="Product Categories" 
          Type="TaxonomyFieldTypeMulti"
          ShowField="Term1033" 
          EnforceUniqueValues="FALSE" 
          Group="XYZ Site Columns" 
          SourceID="http://www.xyz.be/v1" />

This will create your field of the correct type but it will not be mapped to a term set yet. This cannot be done through CAML, but only through code, ideally in the feature receiver:

   field.SspId = termStore.Id;
   field.TermSetId = termSet.Id;
   field.AnchorId = Guid.Empty;

These properties mean:

  • TermSetId: This is the Guid of the term set to which the field is mapped. Users will only be able to tag the item with term(s) from this term set.
  • AnchorId: If the entry point for tagging is a term within the term set, the anchor id needs to be set to the guid of this term. If the entry point for tagging is any term within the term set, this property does not need to be set.
  • SspId: This is the Guid of the term store to which the term set belongs.

You would think that, when you create a field of type TaxonomyFieldTypeMulti, you would be able to store more than one term, but unfortunately that’s not the case.

When you inspect the field in the user interface, you see that the check box Allow multiple values is not checked.

It’s only after having checked this property that you are able to store more than one term using the TaxonomyFieldValueCollection. Unfortunately there is no attribute in CAML to set this checkbox, so you will have to solve this in your feature receiver by setting the AllowMultipleValues property:

if (field.TypeAsString == "TaxonomyFieldTypeMulti")

      field.AllowMultipleValues = true;

As of then you will be able to store multiple terms in the taxonomy field.

December 20, 2013 Posted by | SharePoint 2010, SharePoint 2013 | 2 Comments

Programmatically provisioning a TaxonomyField of type TaxonomyFieldTypeMulti

I had a hard time finding out how I could save multiple terms to a field of TaxonomyFieldTypeMulti using the SharePoint server object model.

The following code worked out for me:

        private static void SetTaxonomyValue(SPListItem item, Term term, string fieldName)

        {

            if (term != null && item != null && item.Fields[fieldName] != null)

            {

                if (item.Fields[fieldName].TypeAsString == "TaxonomyFieldType")

                {

                    TaxonomyField taxonomyField = item.Fields[fieldName] as TaxonomyField;

                    TaxonomyFieldValue taxvalue = new TaxonomyFieldValue(taxonomyField);

                    taxvalue.TermGuid = term.Id.ToString();

                    taxvalue.Label = term.Name;

                    item[fieldName] = taxvalue;

                }

                else if (item.Fields[fieldName].TypeAsString == "TaxonomyFieldTypeMulti")

                {

                    TaxonomyField taxonomyField = item.Fields[fieldName] as TaxonomyField;

                    TaxonomyFieldValueCollection taxvalues = 

item[fieldName] as TaxonomyFieldValueCollection;

                    if (taxvalues == null)

                        taxvalues = new TaxonomyFieldValueCollection(taxonomyField);

                    TaxonomyFieldValue taxvalue = new TaxonomyFieldValue(taxonomyField);

                    taxvalue.TermGuid = term.Id.ToString();

                    taxvalue.Label = term.Name;

                    taxvalues.Add(taxvalue);

                    item[fieldName] = taxvalues;

                }

            }

        }

 

When the taxonomy field is of type TaxonomyFieldTypeMulti, terms are stored as a TaxonomyFieldValueCollection. The trick is that you first have to test whether you already have a term available in the collection or not. If not, you have to initialize the collection, otherwise you will get a “null reference” exception when you try to add a term to the collection.

December 19, 2013 Posted by | SharePoint 2010, SharePoint 2013 | Leave a comment

Programmatically provisioning a term set for navigation

I’ve been experimenting with Managed Metadata and one of the things that I’ve been doing is automating the provisioning a navigation term set using the SharePoint server object model.

In your code you will have to add a reference to the Microsoft.SharePoint.Taxonomy.dll.

First you have to find the correct instance of the term store:

   TaxonomySession session = new TaxonomySession(site, true);

   TermStore termStore = session.TermStores[mmsName];

Then you have to find the correct group to which you want to add your term set. In SharePoint 2010 you could only create groups from within the Central Administration and the term sets in these groups could be accessed by any site collection with the correct permissions.

   termGroup = termStore.Groups[groupName];

But in SharePoint 2013 you can also have a group on site collection level. To retrieve this group you have to use a new method:

   termGroup = termStore.GetSiteCollectionGroup(site);

If you want the term set to be used for navigation, you have to add your term set to the local group:

   TermSet termSet = termGroup.CreateTermSet(termsetName);

And you also have to configure that this term set is going to be used for navigation:

   NavigationTermSet navigationTermSet = NavigationTermSet.GetAsResolvedByWeb(termset, site.RootWeb,

                    StandardNavigationProviderNames.CurrentNavigationTaxonomyProvider);

   navigationTermSet.IsNavigationTermSet = true;

Good to know that the NavigationTermSet class is defined in the Microsoft.SharePoint.Publishing.Navigation namespace of the Microsoft.SharePoint.Publishing.dll.

And then you can start creating the hierarchy of terms that is needed for your site navigation:

   Term term = termSet.CreateTerm(termName, 1033);

In the meantime you can also set the friendly URL.

   NavigationTerm navigationTerm = navigationTermset.Terms.Where(

nt => nt.Id == term.Id).FirstOrDefault();

   navigationTerm.FriendlyUrlSegment.Value = termUrl;

You could also set other properties like the Navigation Hover Text, the Simple Link Url or the Target Url. You could also define whether the term should be excluded for global navigation.

But I had a hard time finding how to change the Navigation Node Title programmatically. It seems that there is no property foreseen for this. I tried to set the Title property of the NavigationTerm but this is a read-only property and reflects the name of the term itself.

When working in the user interface, you can also see that you change the Navigation Node Title, which is set to the name of the term by default:

In Reflector I found that NavigationTerm derives from NavigationTermSetItem and that it has a method named SetCustomizableStringCustomValue(Enum, string) which is not publically accessible. The protected enum is defined as follows:

There you can see that there is indeed a property Title that could map the Navigation Node Title.

I found 2 methods on the Term class that I thought I could use: SetCustomProperty(string, string) and SetLocalCustomProperty(string, string). But both methods created a custom property (which is then visible on the Custom Properties tab).

My friend Tom Van Gaever, who works as a PFE at Microsoft Belgium, pointed out to me that you can set custom properties using the SetLocalCustomProperties method and that properties like the Navigation Node Title have a special name:

  • _Sys_Nav_Title: the Navigation Node Title
  • _Sys_Nav_FriendlyUrlSegment: the Friendly Url, also represented by the FriendlyUrlSegment property of the NavigationTerm class.
  • _Sys_Nav_TargetUrl: the target URL, also represented by the TargetUrl property of the NavigationTerm class.
  • _Sys_Nav_TargetUrlForChildTerms: the target URL for child terms, also represented by the TargetUrlForChildTerms property of the NavigationTerm class.
  • _Sys_Nav_CatalogTargetUrl: the catalog target URL, also represented by the CatalogTargetUrl property of the NavigationTerm class.
  • _Sys_Nav_CatalogTargetUrlForChildTerms: the catalog target URL for child terms, also represented by the CatalogTargetUrlForChildTerms property of the NavigationTerm class.

And that was the key to my solution. By executing the SetLocalCustomProperty method on the _Sys_Nav_Title property, I was able to set the Navigation Node Title:

   term.SetLocalCustomProperty("_Sys_Nav_Title", "core business");

If you use that same method with a property name that does not exist, it will create a custom property (which is then visible on the Custom Properties tab); just like the SetCustomProperty method does.

Save your changes to the term store when you’re finished:

   termStore.CommitAll();

December 13, 2013 Posted by | SharePoint 2013 | 7 Comments

Follow

Get every new post delivered to your Inbox.

Join 172 other followers