Extending Base Xslt Web Parts with custom control bindings

What a long title! Maybe it requires a little bit of explanation. Here it is.

First of all, by Base XSLT Web Parts I mean a whole family of OOTB SharePoint Web Parts that leverage XSLT transformation to produce markup out of XML data coming from a data source. If you dig into the class hierarchy starting, say, from the XsltListViewWebPart or the ContentByQueryWebPart, you’ll find something like this:

image

If you navigate up the hierarchy, you’ll see that several features are implemented by the base DataFormWebPart class. One of these features is the capability to resolve dynamic parameters, i.e. values that come from query string, postback parameters and a few other sources.

More accurately speaking, the sources for dynamic parameters and the logics of parameters substitution are defined by an internal method of DataFormWebPart, named ResolveParameterBindingsToParameterValues.

Below you can find an excerpt of the method implementation:

image

As you can observe, you can actually set the ParameterBindings web part property to an XML string with proper semantics, thus inject dynamic values as XSLT parameters for the rendering transformation.

You can choose from the following sources:

  • Query String
  • Postback Parameters
  • Form Parameters
  • Web Part Connections
  • Web Part Variables
  • Resources
  • Server Variables
  • CAML Variables
  • Controls

Since XSLT and XPath are a powerful query and transformation engine, you will be able to define conditional blocks and data manipulation expressions based on these dynamic bindings.

But there’s more!

Did you notice the last binding source, the Control source?

The way it works is easy: you have to specify the server ID of an ASP.NET control available on the page and the name of one of its properties. The implementation of DataForWebPart will recursively walk the ASP.NET control tree upwards until it finds a control with the specified ID: then, with a couple of Reflection tricks the value of the specified property will be retrieved and made available to the web part rendering.

You can try this behavior adding, say, a TextBox or a DropDownList control on the page and using the value coming from the Text and SelectedValue properties respectively.

What I like most of this approach (hence the reason of this post’s title) is that you are not limited to OOTB web controls: you can “attach” pretty much any kind of control which exposes a readable property in a plain format (complex data types would be serialized using the ToString method, so the usefulness of these properties really depends on the overridden implementation of the ToString method).

By any kind of control I mean any kind of controls, including any custom control that you may develop in order to extend the XSLT* Web Parts capabilities.

From the extensibility point of view, this means that you (as a developer) can write a suite of simple web controls that are not meant to be used for their output markup, but just as a data source for existing, SharePoint web parts. Then, a colleague who is capable of building no-code solutions using SharePoint Designer will be able to leverage your extensions, using CQWPs or CoreResultsWPs without reinventing the wheel (i.e. building everything from scratch).

Nice, isn’t it?

Below you can find a sample implementation of a web control that exposes the current UI culture, and that may be useful in multilingual scenarios where you want your aggregation web parts (CQWPs, etc…) to behave differently according the the selected language.

But you can easily imagine that this technique can be applied in an infinite number of ways!

   1:  namespace GreenTeam.SharePoint2010.Multilanguage
   2:  {
   3:    public class CultureControl : Control
   4:    {
   5:      [WebBrowsable]
   6:      public Int32 UICultureLCID 
   7:      {
   8:        get
   9:        {
  10:          return Thread.CurrentThread.CurrentUICulture.LCID;
  11:        }      
  12:      }
  13:   
  14:      [WebBrowsable]
  15:      public Int32 CultureLCID
  16:      {
  17:        get
  18:        {
  19:          return Thread.CurrentThread.CurrentCulture.LCID;
  20:        }
  21:      }
  22:   
  23:      protected override void Render(HtmlTextWriter writer)
  24:      {
  25:        // Do not emit markup at all!
  26:      }
  27:    }
  28:  }

How to issue Http Web Requests to a SharePoint 2010 site with FBA and SSL

The .NET Framework provides some handy classes that help you manage HTTP communication through requests and response objects.

Everything is plain and simple, as long as you are connecting to a resource endpoint that does not require authentication and is available through an unsecure channel (i.e. HTTP). Which, of course, is not always the case.

I had a requirement to “invoke” a SharePoint 2010 resource (a file, for example) that is protected by Forms Based Authentication and is exposed by a secured SSL channel. The task is slightly more complicated, since it has to be performed by a client that has no easy way to invoke web services using an autogenerated proxy. That is, no “Add Web Reference” available. Indeed, no RAD environment at all.

You can find my solution in the code snippet below. I have written it with PowerShell just as a prototyping tool, it will have to be translated. But anyway some interesting points can be highlighted:

  • I used the .NET Fx API to “ignore” SSL certificate warnings. This may not be an option sometimes, but the solution can be extended adding some certificate chain verification or whatever you need to check. This is done setting the ServerCertificateValidationCallback static property (line 1).
  • I had to invoke the Authentication.asmx SharePoint Web Service in order to authenticate and, then, reuse the authentication cookie in subsequent requests. This is a requirement for FBA access. I handcrafted the SOAP message since there’s no easy-to-use helper/proxy (line 2-24).
  • I finally issued my HTTP request, using the authentication cookie and adding an HTTP header (line 26-28).
   1: [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

   2: $cookieContainer = new-object System.Net.CookieContainer

   3: $authEnvelope = '<?xml version="1.0" encoding="utf-8"?>

   4:                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

   5:                   <soap:Body>

   6:                     <Login xmlns="http://schemas.microsoft.com/sharepoint/soap/">

   7:                       <username>yourusername</username>

   8:                       <password>yourpassword</password>

   9:                     </Login>

  10:                   </soap:Body>

  11:                 </soap:Envelope>';

  12: $encoder = [System.Text.Encoding]::UTF8

  13: $authEnvelopeBytes = $encoder.GetBytes($authEnvelope)

  14:  

  15: $authRequest = [System.Net.HttpWebRequest]([System.Net.HttpWebRequest]::Create("https://yoursite/_vti_bin/authentication.asmx"))

  16: $authRequest.CookieContainer = $cookieContainer

  17: $authRequest.Headers.Add("SOAPAction", "http://schemas.microsoft.com/sharepoint/soap/Login");

  18: $authRequest.ContentType = "text/xml; charset=utf-8";

  19: $authRequest.ContentLength = $authEnvelopeBytes.Length

  20: $authRequest.Method = "POST";

  21: $authRequestStream = $authRequest.GetRequestStream()

  22: $authRequestStream.Write($authEnvelopeBytes, 0, $authEnvelope.Length)

  23: $authRequestStream.Flush()

  24: $authRequest.GetResponse()

  25:  

  26: $request = [System.Net.HttpWebRequest]([System.Net.HttpWebRequest]::Create("https://yoursite/yourcontent"))

  27: $request.CookieContainer = $authRequest.CookieContainer

  29: $request.GetResponse()