|
A large law firm wants to make life a little easier for their hard-working associates.
The senior partners have decided to use their portal for presenting the firm's client
list, integrated with Google Maps and simple ways of communicating with clients.
Although the firm is using WebSphere Portal for serving their portal solution, they
are using Microsoft SharePoint to maintain their client contact list. Also, the
in-house .NET development team has plenty of Visual Studio experience, and the firm
is eager to make good use of their familiarity with Microsoft's IDE and Framework
as the new project kicks off.
The technology parameters in this background story form a common scenario in many
of today's organizations.
Let's take a look at the actual requirements. To make the portal site more useful,
while at the same time spicing it up a little, the page will feature three portlets:
|
|
 |
|
|
Figure 4: The New Project dialog with Visual C# and Visual Basic for Java EE project
types
|
- Law Firm Client List - listing
the clients using a DataList .NET control.
- Google Maps - a map for viewing
the client's location using Google Maps JavaScript API.
- Contact Info - a detailed
view of the selected contact. The user can contact client directly, using the information
stored in SharePoint.
By wiring the portlets together, the map and contact info portlets will automatically
refresh as the user selects different contacts.
This example is written in Visual C#®, but as with all of Mainsoft's
interoperability solutions, it would work just as well using Visual Basic®.
Full source code is available
for download. Before building and deploying the source code, you may modify the
connection string to point to your own SharePoint Server. The connection string
is stored in the file Web.config.
Let's begin with firing up Visual Studio, installed with Mainsoft's Portal Edition.
If you do not already have Mainsoft's Visual Studio add-in installed, contact Mainsoft
at sales@mainsoft.com to receive an evaluation
version of the Portal Edition. I start out by creating a new project using the template
ASP.NET Portal Application, under the
new project type Visual C# for Java EE (see Figure 4). Mainsoft's add-on
also enables you to take an existing ASP.NET solution, and deploy it as a Java EE
application onto WebSphere Portal. To learn more about converting existing .NET
projects to Java, read the article, "Creating WebSphere
Portal Apps Using the Visual Studio IDE".
Connecting the main portlet to the SharePoint list data with the client's contact
details could not be simpler. In the Visual Studio toolbox, locate the SharePointDataSource
control in the Mainsoft SharePoint Integrator for WebSphere Portal group. Add the control to the design
surface of ClientList.aspx, the file that makes up the default view mode
for the new portlet.
|
|
 |
|
|
Figure 5: The SharePointDataSource Web control
|
The SharePointDataSource control features a smart tag (a small arrow in the upper
right corner, see Figure 5) that enters the configuration wizard for the SharePoint
data source. Using the wizard, customize the SharePoint data that will be retrieved.
The first page of the wizard hosts the connection string parameter. You have the
option to specify different authentication schemes at runtime and design time, which
you need to do if you are using the WebSphere Credential Vault since it is not accessible
from within Visual Studio. Typically, you authenticate using either a trusted connection
(with your Windows user credentials) or a WebSphere Portal Credential Vault, specifying
the resource name.
|
|
 |
|
|
Figure 6: Configuring the connection
|
Credential Vault authentication requires a few more lines of code, compared to trusted
connections that work out-of-the-box. Credential Vault authentication comes in two
flavors: user and admin. The site administrator can add a shared admin credential
slot, containing valid SharePoint login information, which is available to all WebSphere
Portal users. Private credential slots are defined by and for each user. I am using
the private Credential Vault in the sample application.
I fill in the SharePoint URL and the private Credential Vault's resource name (here
named "SharePointAccess") in the new data source dialog (see Figure 6).
The wizard's second page is where you specify the SharePoint list you will display.
Next up you have the option to custom-select the results using standard CAML query
syntax in the wizard. Here, I use a custom query, because I only want U.S. customers
returned. In the wizard's fifth page, I define the query in Figure 7, using the
Visual Query Designer:
You can construct very complex queries using the Visual Designer, and in case you
want to fine-tune them, you can use the IntelliSense-enabled XML Editor. Later on,
you can modify any aspects of your SharePoint connection using the control's properties.
Now, I need a way of displaying the data fetched in the SharePointDataSource. I
choose to use a standard ASP.NET DataList Web control, which I connect to the SharePoint
data source in the same way as I would connect it to any other standard .NET data
source.
|
|
 |
|
|
Figure 7: The CAML query defined in the Visual Designer
|
One of the differences between a DataList control and the more common GridView control
is that the DataList will not display any columns by default (GridView displays
all columns from the data source). Using the DataList's Edit Template command, I
choose the data rows. Here, I'll present the name and company of each client in
our SharePoint list using the format "<full name> at <Company>". This
is achieved by adding a Label control to the DataList's ItemTemplate. In the Label's
DataBindings properties, I use Custom Binding with the following Code Expression:
Eval("FullName") + " at
" + Eval("Company")
|
|
 |
|
|
Figure 8: The Portlet Designer
|
The Eval() method takes the name of the SharePoint data source column as a parameter
and returns the value of the current row.
I've also added two hidden labels that will be used for sending data to other portlets.
One contains the current address of the client, while the other holds the e-mail
address. I will return to these fields when it is time to use them. I also add an
ImageButton to the ItemTemplate, which will ensure that the user can select items
in the list. Any command-type button control (Button, LinkButton, or ImageButton)
in the ItemTemplate enables selections.
Now, let's make it easier for end users to register their SharePoint credentials
in WebSphere's Credential Vault on the first visit. Even though it is possible for
portal users to add private Credential Vault slots using the administration interface,
it is much more user-friendly to prompt them for their SharePoint usernames and
passwords using the portlet's Edit mode. Add Edit mode to the portlet by
opening the WEB-INF\portlet.xml file, which shows the project portlets'
properties using Mainsoft's Portlet Designer (see Figure 8). Here, you can configure
the portlet completely, without having to learn the JSR 168 portlet specification.
In the Portlet Designer, click the "..." button in Portlet Modes,
enable Edit mode, and specify a new .aspx filename to associate with Edit
mode. The new ASPX file is, at this point, generated automatically.
I add the SaveCredentials Web control (available from the Mainsoft SharePoint Integrator for WebSphere Portal control group) to the new form (see Figure 9).
In the control's properties, I set ResourceName to "SharePointAccess", which
is the same resource name I configured the SharePointDataSource to use to retrieve
data at runtime.
|
|
 |
|
|
Figure 9: The SaveCredentials Web control on the designer surface
|
First-time visitors to the portlet will run into the problem that when they will
load the View mode (which is the default mode) no user credential will have been
set and a SharePointDataSource exception will be thrown. I need to catch this exception
and let the user know that before the portlet can be used, the SharePoint credentials
must first be set in the Edit mode (named Personalize in the browser). One
way to skin this cat is to override the Page_Error() method and display
an informative message. Using this technique, I add the following implementation
to ClientList.aspx.cs, containing the portlet's View mode code:
public void Page_Error(object sender, EventArgs e)
{
Exception ex = Context.Error.GetBaseException();
if (ex is UnauthorizedAccessException || ex.Message.Contains("Unauthorized"))
{
Context.ClearError();
Context.Response.Write("<b>SharePoint authentication
failed.</b><br />Please add your SharePoint login information in the
portlet's Personalize mode to access the client list.");
Context.Response.End();
}
}
|
|
 |
|
|
Figure 10: The SharePoint client list running on WebSphere Portal
|
That's it for the first iteration of development. Running the application at this
point will present us with the following portlet, after entering the SharePoint
credentials (see Figure 10):
This is actual data from the SharePoint server, aggregated in WebSphere Portal within
a single sign-on environment. It was all accomplished in just a few simple steps,
manually adding only a few lines of error-handling code!
So far, so good. Now it's time to make things more interesting by adding IPC support,
which enables the portlets to communicate with each other. To enable the client
list portlet to transmit information about the selected item, I need to make the
following modifications:
- Specify the datatype and
other meta-information of the transmitted data in a Web Services Description Language
(WSDL) file.
- Map the portlet to the WSDL
file.
- Add parameters to the PostBackUrl
property for the DataListItems' ImageButton.
- Handle the DataList's ItemCommand
event to transmit the actual value over IPC.
Knowing that our two target portlets will render a map and display contact information,
using the wpPerson Web control, I specify two output parameters from the portlet.
Below are two snippets from the WSDL file, which I add to the project:
[…]
<types>
<xsd:schema targetNamespace="http://www.ibm.com/wps/p2psample3">
<xsd:simpleType name="AddressType">
<xsd:restriction base="xsd:string">
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
</types>
[…]
<binding
name="GoogleMapSearchBinding"
type="tns:GoogleMapSearch_Service">
<portlet:binding/>
<operation name="ClientDetails">
<portlet:action name="ClientDetailsAction"
type="standard"
caption="ClientDetails.action"
description="Get the details of the selected client"
actionNameParameter="ClientDetailsActionParam"/>
<output>
<portlet:param name="ClientDetailsText"
partname="ClientDetails_Text"
boundTo="request-attribute"
caption="ClientDetails.text"/>
</output>
</operation>
</binding>
Going into the details of WSDL is outside the scope of this text. If you want to
learn more, read Using inter portlet communication in Mainsoft's documentation.
All you really need to know for now is that the "types" element specifies a string
datatype, and that data being communicated between portlets must use the same datatype
on the sending and receiving end. This datatype is used to bind two output parameters
from the LawFirmClient portlet, one is the selected item's work address ("WorkAddressActionParam"),
and the other the name and email address ("ClientDetailsActionParam") separated
with a semi-colon. The full contents of the WSDL file are available in the download
of the project source code.
|
|
 |
|
|
Figure 11: Directing WebSphere Portal to associate a WSDL file with a portlet
|
I use the Portlet Designer to connect the WSDL file to the portlet. A new key value
pair in the Preferences collection tells WebSphere Portal to make the association
(see Figure 11).
In order to enable the client list portlet to send information to its fellow portlets,
the event fired when a user selects a DataListItem needs to be handled. As all underlying
IPC infrastructure is in place, I only need to add a few lines of code to set the
correct value of the appropriate IPC parameter. I add a handler to the DataList's
ItemCommand event, which is fired when the item selection changes:
protected void DataList1_ItemCommand(object source, DataListCommandEventArgs e)
{
string name = ((Label)e.Item.FindControl("NameLabel")).Text;
string address = ((Label)e.Item.FindControl("HiddenAddress")).Text;
string email = ((Label)e.Item.FindControl("HiddenEmail")).Text;
PortletRequest.setAttribute("ClientDetailsText", name + ";" + address + ";" + email);
}
The implementation of the event takes the name, work address and e-mail address
for the selected client and inserts it as string into a PortletRequest attribute.
The PortletRequest object is used to carry the IPC data between the portlets. I
am using a helper class (Client) for building a string from the client details.
To enable the IPC action parameter, I need to add an input field, which I make hidden.
The input field contains the name and actionNameParameter from the WSDL's action
element as follows:
<input type="hidden" name="ClientDetailsActionParam" value="ClientDetailsAction" />
I have now enabled the portlet with the ability to send IPC data to other portlets.
Next up, I will add two additional portlets and have them receive and parse the
data sent by the client list portlet, and render their own representation of it.
|
|
 |
|
|
Figure 12: Configuring additional portlets - LawFirmClients, GoogleMaps and ClientDetails
|
The Portlet Designer is used to create and configure two additional portlets in
our portal application. The Add Portlet button creates a new portlet. I let
each portlet's View mode be implemented in two new ASPX pages by setting the View
mode in the Portlet Modes property (see Figure 12).
The Google Maps portlet will use the Google JavaScript API to render the client's
location in a portlet. In order to embed the Google Maps service in a portlet, you
will have to sign up for a Google Maps API key. This key will be used in the portlet's
JavaScript code to initialize the API access. Although the downloadable LawFirmClient
application contains a Google Maps API key, I recommend that you apply for your
own and use it.
A minimalist HTML design is sufficient to implement the map portlet. A single div
tag with the "map" ID is the placeholder the Google Maps API will use to render
the requested map. I need to handle the IPC data sent from the client list portlet
and resend it to the Google Maps API as a JavaScript function parameter.
The first step is to add a WSDL file for the map portlet, containing the same datatype
as the WSDL file for the client list. The main difference is that this file defines
input elements instead of output elements. Second I create a new mapping between
the WSDL file and the portlet using the Portlet Designer as previously described.
Third, I add the logic for retrieving and processing the parameter value. This step
is added to the map portlet's class as follows:
public partial class GoogleMaps : Mainsoft.Web.UI.PortletPage
{
protected string searchAddress = "";
protected void Page_Load(object sender, EventArgs e)
{
if (IsInProcessAction)
SetRenderParameter("RenderClientDetailsText",
Request.Params["ClientDetailsText"]);
}
}
PageLoad() is called twice on callbacks containing IPC data. The first call contains
the IPC data as a HTTPRequest parameter, while the second is the standard PageLoad()
call. In order to separate the logic between the two calls, you can use IsInProcessAction,
a property inherited from Mainsoft.Web.UI.PortletPage. The IsInProcessAction
property is true for the IPC call and false for the standard call. In the source
code above, the member variable searchAddress is used to store the address
retrieved from IPC in the HTTPRequest parameter "GoogleMapSearchText" (the name
is defined in the WSDL file). This value is then injected into the JavaScript code
communicating with the Google Maps API, using the code below that I insert into
the markup (which is only executed during the render phase). It also sets the input
field to display the actual address text in the portlet.
<%
string clientData = (string)Request.Params["RenderClientDetailsText"];
if (!String.IsNullOrEmpty(clientData))
{
String[] properties = clientData.Split(';');
if (properties.Length == 3)
this.searchAddress =
properties[(int)LawFirmClients.eClientDetails.Address];
}
%>
|
|
 |
|
|
Figure 13: The wpPerson Web control
|
For the ClientDetails portlet, which will be used to enable communication, I use
the Mainsoft's wpPerson Web control (see Figure 13). The wpPerson control provides
contextual collaboration functionality related to a named person, and it is similar
in syntax and behavior to the WebSphere Portal person tag. It displays the online
status for a portal user, taking into account Domino and Extended products and servers
installed and enabled in the portal environment.
Configuring the ClientDetails portlet follows the same steps as the map portlet.
I add a WSDL file, which defines the IPC input bindings, hook it up to the portlet
using the Portlet Designer, and add the logic. Do you remember that the two hidden
labels in the client list portlet contain the work address and e-mail address of
the selected user? This is the data, together with the client's full name, that
is being sent over IPC. I encode the data as a string, and on the receiving end,
the string is decoded, and it uses the fields relevant for the target portlet. The
code below shows how the ClientDetails portlets decodes the IPC string and uses
the data to set the control’s properties. The first section comes from the code-behind,
while the second code snippet is inserted into the markup:
protected void Page_Load(object sender, EventArgs e)
{
SetRenderParameter("RenderClientDetailsText",
Request.Params["ClientDetailsText"]);
}
<%
string clientData = (String)Request.Params["RenderClientDetailsText"];
if (!String.IsNullOrEmpty(clientData))
{
String[] properties = clientData.Split(';');
if (properties.Length == 3)
{
WpPerson1.DisplayName =
properties[(int)LawFirmClients.eClientDetails.Name];
WpPerson1.Value =
properties[(int)LawFirmClients.eClientDetails.Email];
}
}
%>
|
|
 |
|
|
Figure 14: The Portlet Wiring Tool in WebSphere Portal Server
|
The implementation is now ready for end-user consumption. It is important to notice
that once the application has been deployed, the portal user needs to activate the
wires himself. This is done by using the Portlet Wiring Tool (see Figure 14), accessible
from the Wires tab under Edit Page Layout in WebSphere Portal Server.
After adding the two wires (one from the client list to the map; the other one from
the client list to the client details), we are good to go. Figure 15 shows a screen-shot
of WebSphere Portal running our application with SharePoint contents, Google Maps
data and the WebSphere Portal person-enabling communication features.
|
|
 |
|
|
Figure 15: The final result
|
Conclusion
Thank you for reading this walkthrough! The full source code for the LawFirmClient
project can be downloaded from the menu in the left-hand column of this page. You
are welcome to contact Mainsoft at sales@mainsoft.com
to receive an evaluation version of Mainsoft, Portal Edition, which contains SharePoint
integration, and see for yourself what it's like to integrate SharePoint data with
enterprise Java applications, without having to rely on complex interoperability
layers or external development teams.
|