How to: Create a Custom External Application Provider
This topic provides an overview of the process of creating a custom External Application Provider (EAP).
Applies to: SharePoint Foundation 2010
Creating a custom EAP requires deriving new classes from one or both of the two abstract classes:
SPExternalApplicationRequestResult – There are two scenarios in which you would implement a SPExternalApplicationRequestResult-derived class:
The site and request forwarder where the external application is installed require that all results that it receives from Microsoft SharePoint Foundation include a client hash that the request forwarder can verify to ensure that the result has not been tampered with.
You want to change the way the SilverlightWebPart, or some custom Web Part that hosts a non-SharePoint application, is rendered; for example, you want to render custom chrome around the Web Part.
SPExternalApplicationProvider – You will need to implement this class in any of the following scenarios:
Any situation in which you are implementing a class derived from the SPExternalApplicationRequestResult class (see above).
The External Application XML for the Web Part contains custom markup that needs to be read and processed.
You want special logic to run when the Web Part’s child control is created.
You want to customize the UI of the SilverlightToolPart or change the application registration page that opens when the Configure button on the tool part is clicked.
Important
When considering whether to create a custom EAP, keep in mind that there can be only one EAP for all the application-hosting Web Parts in all the SharePoint Foundation Web applications that are children of a specified Web service.
To Implement a Request Result Class
In Microsoft Visual Studio 2010, start an empty SharePoint project.
Add a class item to the project.
Set the class to inherit from SPExternalApplicationRequestResult.
If you do not want a client hash, implement the ClientHash property to return null. If you want to include a client hash in the results that are sent back to the external application, implement the ClientHash property as a wrapper around a private backing field that is a Byte array.
private Byte[] clientHash; public override Byte[] ClientHash { get { return clientHash; } }
Private clientHash_Renamed() As Byte Public Overrides ReadOnly Property ClientHash() As Byte() Get Return clientHash_Renamed End Get End Property
If you are using a client hash, create a constructor that takes a parameter of type SPExternalApplicationRequestProperties and a second parameter that is a byte array (which represents the client salt). This constructor should use the RequestTokenPrefix property of the SPExternalApplicationRequestProperties object and the byte array as input when creating a client hash value. The byte array must be a value that is provided by server that hosts the external application and the algorithm used must be the same one that will be used by the external application to create its copy of the client hash. Consider using the classes of the System.Security.Cryptography namespace such as HashAlgorithm or SHA512Managed. The following is an example. To use the SHA512Managed class as this example does, you must add a using (Imports in Visual Basic) statement for the System.Security.Cryptography namespace to the code file.
public CustomRequestResult() { } public CustomRequestResult(SPExternalApplicationRequestProperties externalAppRequest, byte[] clientSalt) { string prefix = externalAppRequest.RequestTokenPrefix; int nCount = Encoding.Unicode.GetByteCount(prefix); nCount += clientSalt.Length; byte[] bytes = new byte[nCount]; nCount = Encoding.Unicode.GetBytes(prefix, 0, prefix.Length, bytes, 0); for (int i = 0; i < clientSalt.Length; i++) { bytes[nCount + i] = clientSalt[i]; } // Compute the hash value SHA512Managed sha512 = new SHA512Managed(); clientHash = sha512.ComputeHash(bytes); }
Public Sub New() End Sub Public Sub New(ByVal externalAppRequest As SPExternalApplicationRequestProperties, ByVal clientSalt() As Byte) Dim prefix As String = externalAppRequest.RequestTokenPrefix Dim nCount As Integer = Encoding.Unicode.GetByteCount(prefix) nCount += clientSalt.Length Dim bytes(nCount - 1) As Byte nCount = Encoding.Unicode.GetBytes(prefix, 0, prefix.Length, bytes, 0) For i As Integer = 0 To clientSalt.Length - 1 bytes(nCount + i) = clientSalt(i) Next i ' Compute the hash value Dim sha512 As New SHA512Managed() clientHash = sha512.ComputeHash(bytes) End Sub
If the only part of the SPExternalApplicationRequestProperties class that your derived class uses is the RequestTokenPrefix property, then the constructor could be written to take a String object as the first parameter and then calling code (which is the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method discussed below) simply passes the RequestTokenPrefix property to it. Another option is to design the calling code to create the client hash before it constructs an object of your derived type. In that case, the constructor of the type can be designed to take the hash value itself as a byte array parameter and write immediately to the clientHash backing field.
If you do not want to customize how the Silverlight Web Part is rendered or how some custom Web Part that hosts a non-SharePoint application, is rendered, implement the GetContentControl(String) method to return null. But if you do want to customize how the Web Part is rendered, implement the method to return a Control that will be the sole child control of the Web Part. Typically this will be a Literal containing HTML markup.
To Implement an EAP Class
Add a class item to the project.
Set the class to inherit from SPExternalApplicationProvider.
If you want to customize the UI of the SilverlightToolPart, implement the GetRegistrationInformation(SPWeb) method. You can change any of the five properties of the SPExternalApplicationRegistrationInformation object that the method returns. For example, if you wanted to substitute a custom registration page, assign the URL of your custom page to the HyperlinkUrl property. You can use inline code or code behind the custom page to provide other services to users. For example, your code could read the Application XML and see if it specifies an application principal name. If so, the code checks to see if that application principal user exists and, if not, creates it.
The following example changes the name of the button on the page from "Configure" to "Register", the instructional text for the button, and the size of the dialog that it opens. It also causes the button to open a custom alternative application registration page. (The default registration page is newslwp.aspx.)
public override SPExternalApplicationRegistrationInformation GetRegistrationInformation(SPWeb web) { SPExternalApplicationRegistrationInformation info = new SPExternalApplicationRegistrationInformation(); info.Description = "To register a Silverlight application (.xap), click Register"; info.DialogHeight = 600; info.DialogWidth = 500; string url = web.ServerRelativeUrl; if (!url.EndsWith("/")) { url = url + "/"; } url += "_layouts/alternateReg.aspx"; info.HyperlinkText = "Register"; info.HyperlinkUrl = url; return info; }
Public Overrides Function GetRegistrationInformation(ByVal web As SPWeb) As SPExternalApplicationRegistrationInformation Dim info As New SPExternalApplicationRegistrationInformation() info.Description = "To register a Silverlight application (.xap), click Register" info.DialogHeight = 600 info.DialogWidth = 500 Dim url As String = web.ServerRelativeUrl If Not url.EndsWith("/") Then url = url & "/" End If url &= "_layouts/alternateReg.aspx" info.HyperlinkText = "Register" info.HyperlinkUrl = url Return info End Function
If you did not derive a class from SPExternalApplicationRequestResult, then you can implement OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) to simply return null. Otherwise, you must give this method a substantial implementation so that your request result class is constructed and used.
The OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method is called by the Web Part’s CreateChildControls method. Specifically, the CreateChildControls method does the following:
Constructs a SPExternalApplicationRequestProperties object from the External Application XML that was used to register the Web Part and from information about the current Web site.
Passes the a SPExternalApplicationRequestProperties object to the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method.
Receives a SPExternalApplicationRequestResult-derived object that is returned by the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method.
Uses the GetContentControl(String) method of the request result object to obtain the Web Part’s sole child control.
Figure 1: Calls made by the CreateChildControls method.
If, for any reason, the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method returns null, as it does in the default EAP that is built into SharePoint Foundation, then the CreateChildControls method will need to render a default child control. This is what the CreateChildControls() method of the built-in SilverlightWebPartclass does.
Therefore, the primary job of your implementation of the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method is to call the constructor of your SPExternalApplicationRequestResult-derived class and return the constructed object. Here are some points to keep in mind when developing your override of the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method:
If your SPExternalApplicationRequestResult-derived object has a non-null ClientHash property, then OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) should call the nondefault constructor that you created for your SPExternalApplicationRequestResult-derived class. OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) should pass to this constructor the same SPExternalApplicationRequestProperties object that it received as a parameter when it was called.
Even if your request result class does not give a substantial implementation to ClientHash (and hence has only a default constructor), you still need to give the Web Part’s CreateChildControls method a SPExternalApplicationRequestResult object so it can call the GetContentControl(String) method. This is why you must implement the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method, if you have derived a class from SPExternalApplicationRequestResult.
An implementation of the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method can do other work as well; for example, it can read custom properties in the External Application XML.
In the following example, the overridden OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) calls the nondefault constructor of a SPExternalApplicationRequestResult-derived class called CustomRequestResult. For more about this nondefault constructor see the procedure To Implement a Request Result Class earlier in this topic.
public override SPExternalApplicationRequestResult OnApplicationWebPartCreateChildControls( SPExternalApplicationRequestProperties args) { SPExternalApplicationRequestResult reqRes = CustomRequestResult(args, saltFromApplication); return reqRes; }
Public Overrides Function OnApplicationWebPartCreateChildControls(ByVal args As SPExternalApplicationRequestProperties) As SPExternalApplicationRequestResult Dim reqRes As SPExternalApplicationRequestResult = CustomRequestResult(args, saltFromApplication) Return reqRes End Function
Identifying Your EAP to the Web Service
The process of identifying to SharePoint Foundation your custom EAP is virtually the same as the process for enabling an EAP as described in How to: Enable an External Application Provider. The only difference is that instead of simply enabling an EAP, you construct an object of your EAP type and assign it to the ExternalApplicationSettings.Provider property. The following is an example, where ContosoEAP is a class derived from SPExternalApplicationProvider.
ContosoEAP exAppProvider = new ContosoEAP();
SPWebService.ContentService.ExternalApplicationSettings.Provider = exAppProvider;
SPWebService.ContentService.ExternalApplicationSettings.Enabled = true;
SPWebService.ContentService.Update();
Dim exAppProvider As New ContosoEAP()
SPWebService.ContentService.ExternalApplicationSettings.Provider = exAppProvider
SPWebService.ContentService.ExternalApplicationSettings.Enabled = True
SPWebService.ContentService.Update()
Tip
The line that sets the Enabled property is not needed if external application management has already been enabled for the Web service, but it does no harm. If management has not previously been enabled, including it obviates the need for the procedure in How to: Enable an External Application Provider. However, the converse is not the case, even if the default EAP is being used, the management enabling code must be executed.
You have available all the methods for running the preceding lines of code that are mentioned in How to: Enable an External Application Provider. The following example shows how to run the code with the Windows PowerShell Add-Type cmdlet in a Windows PowerShell command-line interface script.
To Identify Your EAP to the Web Service
Add the following to a text file.
Add-type @" using System; using Microsoft.SharePoint.Administration; namespace ContosoCmdlets public class EAPIdentifier { public static void IdentifyEAP() { ContosoEAP exAppProvider = new ContosoEAP(); SPWebService.ContentService.ExternalApplicationSettings.Provider = exAppProvider; SPWebService.ContentService.ExternalApplicationSettings.Enabled = true; SPWebService.ContentService.Update(); } } "@ -Language CsharpVersion3 [ContosoCmdlets.EAPIdentifier]::IdentifyEAP()
Save the file as EAPIdentify.ps.
Run the script in a Windows PowerShell window.