Second Life of a Hungarian SharePoint Geek

February 24, 2011

SharePoint menus lost their functionality

Filed under: IE add-in, SharePoint, SP 2010 — Tags: , , — Peter Holpar @ 23:34

In the last few days I was developing a SharePoint 2010 solution, including a feature with a custom action.

In the middle of the testing / fine tuning process I suddenly realized that my custom action does not work anymore. When I clicked on the menu simple nothing happened.

Restarting the browser (Internet Explorer 8 in my case) or clearing IE cache to remove potentially corrupted cached .js files does not help nor does the “standard” IISRESET and server restart to exclude server side issues.

JavaScript was enabled, other web pages worked as expected, even the ribbon controls on my SharePoint pages worked like before.

However, built-in menus in the edit control block (ECB), Site Actions and Welcome Menu also suffered from the same problem.

To decide whether it is a client or server side issue, I visited another server from my machine. This server runs MOSS 2007 but I found the same problem with the menu when visiting from my developer server.

It gave me some hope and help that when started my browser InPrivate or started its x64 version the sites showed their normal behavior.

image 

At this point I started suspecting for JavaScript interference with installed IE add-ons. To start Internet Explorer with all add-ons disabled, you can use the –extoff command prompt parameter. See Internet Explorer Command-Line Options for details. The image below shows Internet Explorer started in Add-ons disabled mode.

image

I found that disabling the add-ons solved the issue, so the next step was to locate the add-on that was responsible for the behavior. I disabled the add-ons one by one in the Tools / Manage Add-ons menu in IE 8.

image

In my case a DivX add-on was accountable for the issue. A few days ago I installed a DivX codec that included add-ons as well, but the behavior started only yesterday, probably when I restart IE the first time after the codec installation.

February 16, 2011

How we can set rating in the name of other users from code?

Filed under: Rating, Social computing, SP 2010 — Tags: , , — Peter Holpar @ 22:47

Two months ago I wrote about my unsuccessful attempts trying to impersonate users in call into the SharePoint 2010 social API. At that time I was not able to show you the way to achieve this, and asked the help of the readers to point to the right direction.

Fortunately, Andres Ciampo wrote a comment to that post including a code snippet that seems to do the job of the impersonation.

The trick in this solution is to create a custom, impersonated HttpContext, that is set as the current context and used to create the SPServiceContext that is passed to the SocialRatingManager constructor, as illustrated in this code:

  1. private void SetRatingForListItem(SPListItem listItem, int ratingValue, String ratingTitle, SPUser user)
  2. {
  3.  
  4.     IPrincipal impersonationPrincipal = new WindowsPrincipal(new WindowsIdentity(GetUpn(user)));
  5.  
  6.     HttpRequest request =
  7.     new HttpRequest(string.Empty, listItem.Web.Url, string.Empty);
  8.  
  9.     HttpResponse response =
  10.     new HttpResponse(
  11.     new System.IO.StreamWriter(new System.IO.MemoryStream()));
  12.  
  13.     HttpContext impersonatedContext =
  14.     new HttpContext(request, response);
  15.  
  16.     impersonatedContext.User = impersonationPrincipal;
  17.     impersonatedContext.Items["HttpHandlerSPWeb"] = listItem.Web;
  18.  
  19.     HttpContext.Current = impersonatedContext;
  20.  
  21.     SPServiceContext serviceContext = SPServiceContext.GetContext(impersonatedContext);
  22.     Uri uri = new Uri(String.Format("{0}/{1}", listItem.Web.Url, listItem.Url));
  23.  
  24.     SocialRatingManager ratingManager = new SocialRatingManager(serviceContext);
  25.     ratingManager.SetRating(uri, ratingValue, ratingTitle);
  26.     ratingManager.PropagateRating(uri);
  27.  
  28. }

I verified the result using the GetRatingsAndCommentsForListItem method (see my former post for its code) and it proves the rating was really set in the name of the impersonated user.

Andres, thanks again for the idea and sharing the code with us!

February 9, 2011

How to query external lists on the server side using CAML and alter the external data?

Filed under: BCS, CAML, External list, SP 2010 — Tags: , , , — Peter Holpar @ 08:38

In my former post I’ve wrote about how to query SharePoint 2010 BCS external lists using client side code. Now I try to show how to do it on the server side.

For this example I used the external links sample demonstrated in a this post. Although the download for that post contains only a single sample link having SharePoint in its title, you need to create more such links to see the real effect of the code below. I used that code in a server side console application, but after minor modifications it can be used in web context as well.

  1. String siteUrl = "http://sp2010";
  2.  
  3. using (SPSite site = new SPSite(siteUrl))
  4. {
  5.     using (SPWeb web = site.OpenWeb())
  6.     {
  7.  
  8.         SPList list = web.Lists["External Links"];
  9.  
  10.         // query items
  11.         SPQuery query = new SPQuery();
  12.         query.Query =
  13.             @"<Where>
  14.                 <Contains>
  15.                     <FieldRef Name='Title' />
  16.                     <Value Type='Text'>SharePoint</Value>
  17.                 </Contains>
  18.               </Where>
  19.               <OrderBy>
  20.                    <FieldRef Name='Title'/>
  21.               </OrderBy>";
  22.         query.ViewFields =
  23.             @"<ViewFields>
  24.                    <FieldRef Name='Title'/>
  25.               </ViewFields>";
  26.         // RowLimit seems to have no effect
  27.         query.RowLimit = 1;
  28.  
  29.         SPListItemCollection items = list.GetItems(query);
  30.  
  31.         foreach (SPListItem item in items)
  32.         {
  33.             Console.WriteLine("Title: '{0}'", item["Title"]);
  34.         }
  35.  
  36.     }
  37. }

As you can see, the code is just like for a standard SharePoint list. You have to include the fields you use in the code in the ViewFields node of the CAML query as FieldRef. Similar to the client side example, specifying the RowLimit seems to have no effect, that is very sad, but true.

If your model allows that (it is not read-only and has the necessary methods), you can use code to alter (create, update, delete) your external data through the external list as well. In our case, the following code snippet will create a new link in the file system:

  1. newItem["Link"] = new SPFieldUrlValue { Url = "http://www.company.com&quot;, Description = "Link item from code" };
  2. newItem.Update();
  3. list.Update();

In this case we had to fill only the Link field with the link title and URL, as external list item Title field will be determined in our .NET assembly BCS connector class based on the link title (see the original post for details). It means that the fields you have to fill depend on the nature of the connector and the properties of the external system.

How to query external lists on the client side using CAML?

Filed under: BCS, CAML, Managed Client OM, SP 2010 — Tags: , , , — Peter Holpar @ 08:05

One of the great features of SharePoint 2010 BCS External List concept is that it enables you to publish and access external data similar way as in the case of standard SharePoint list (I wrote similar, because there are significant exceptions as well). It includes the programmatic access either.

Despite of the similarities, I have found forums questions that show it is not always trivial to achieve your goals. In this post I show you a code example that illustrates how one can filter the external data using managed client object model through a  CAML query. For this example I’ve created an external list on my server that connects to the Customers table of the well-known Northwind database. You can find several posts about how to create the external content type (ECT) for you external list, for example here.

Once you have your external list created (called Northwind Customers in my case), you can try this code from your client project:

  1. String siteUrl = "http://sp2010&quot;;
  2.  
  3. ClientContext clientContext = new ClientContext(siteUrl);
  4.  
  5. List list = clientContext.Web.Lists
  6.     .GetByTitle("Northwind Customers");
  7. CamlQuery camlQuery = new CamlQuery();
  8. // When using a CAML query for external list
  9. // one should include all referenced fields in ViewFields
  10. // otherwise a "The given key was not present in the dictionary" exception is thrown
  11. // RowLimit seems to have no effect            
  12. camlQuery.ViewXml =
  13.     @"<View>
  14.         <Query>
  15.           <Where>
  16.             <Eq>
  17.               <FieldRef Name='City'/>
  18.               <Value Type='Text'>London</Value>
  19.             </Eq>
  20.           </Where>
  21.           <OrderBy>
  22.             <FieldRef Name='ContactName'/>
  23.           </OrderBy>
  24.         </Query>
  25.         <ViewFields>
  26.           <FieldRef Name='CustomerID'/>
  27.           <FieldRef Name='ContactName'/>
  28.           <FieldRef Name='CompanyName'/>
  29.           <FieldRef Name='City'/>
  30.         </ViewFields>
  31.         <RowLimit>1</RowLimit>
  32.     </View>";
  33.  
  34. // include referenced field here as well
  35. ListItemCollection listItems = list.GetItems(camlQuery);
  36. clientContext.Load(
  37.      listItems,
  38.      items => items
  39.          .Include(
  40.             item => item["CustomerID"],
  41.             item => item["CompanyName"],
  42.             item => item["ContactName"],
  43.             item => item["City"]));
  44. clientContext.ExecuteQuery();
  45.  
  46. // display the result
  47. foreach (ListItem listItem in listItems)
  48. {
  49.     Console.WriteLine("CustomerID: '{0}', CompanyName: '{1}', ContactName: '{2}'",
  50.         listItem.FieldValues["CustomerID"],
  51.         listItem.FieldValues["CompanyName"],
  52.         listItem.FieldValues["ContactName"]);
  53. }
  54.  
  55. Console.Write("Press Enter to continue!");
  56. Console.ReadLine();

The code filters customers that have London specified in the City field and order them by the value of the ContactName field.

Important things to note:

  • You must include all fields you use either to filter, order in CAML query, or to display later in your code into both the ViewFields node of CAML query as FieldRef and load into the ClientContext as illustrated above. Otherwise you will receive a "The given key was not present in the dictionary" exception when referencing the field later.
  • Specifying the RowLimit seems to have no effect, all of the matching items are returned by the query (I found similar info in this forum thread). That is not very nice when working with external lists having large item count.

February 1, 2011

How to get a BCS metadata catalog without specifying a URL?

Filed under: BCS, SP 2010 — Tags: , — Peter Holpar @ 23:06

It was the original question in this MSDN forum thread.

As you probably know, we can get a reference for a BCS catalog from client side code (Microsoft.SharePoint.BusinessData.Administration.Client) like this:

AdministrationMetadataCatalog catalog = AdministrationMetadataCatalog.GetCatalog(siteUrl);

On the server side (Microsoft.SharePoint.BusinessData.Administration) there are two different options. If the process runs in the SharePoint context, you can use a code that does not require the URL:

BdcService service = SPFarm.Local.Services.GetValue<BdcService>();
AdministrationMetadataCatalog catalog = service.GetAdministrationMetadataCatalog(SPServiceContext.Current);

However if your code runs out of SharePoint context, you need a URL again to get the SPServiceContext reference:

SPServiceContext.GetContext(siteUrl)

In this specific question we had to solve the puzzle for the server side, out of SharePoint server context variation, to be able to use the code from an STSADM command.

Although my first reply (one has to provide some kind of context for the BdcSerivce to be able to return a catalog to work with) remained true, it turned out one can get a service context without specifying a site URL, and it can be done using the following code snippet:

  1. private AdministrationMetadataCatalog GetBdcCatalog()
  2. {
  3.     AdministrationMetadataCatalog catalog = null;
  4.  
  5.     try
  6.     {
  7.         BdcService bdcService = SPFarm.Local.Services.GetValue<BdcService>();
  8.         SPServiceApplication serviceApp = bdcService.Applications.FirstOrDefault();
  9.  
  10.         if (serviceApp != null)
  11.         {
  12.             SPServiceContext context = SPServiceContext.GetContext(serviceApp.ServiceApplicationProxyGroup, SPSiteSubscriptionIdentifier.Default);
  13.             catalog = bdcService.GetAdministrationMetadataCatalog(context);
  14.         }
  15.     }
  16.     catch (Exception ex)
  17.     {
  18.         // do error handling
  19.     }
  20.  
  21.     return catalog;
  22.  
  23. }

A bit tricky, but seems to work as expected.

Theme: Shocking Blue Green. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 42 other followers