Tuesday 26 February 2008

Webparts in MOSS 2007 - Part 4

The examples we have seen until now talks about webparts that worked independently (and good :)). Assuming we need to get two webparts to talk with each other, you need to follow this :

1.) Setup a data object interface which would be shared by the two webparts.
public interface ISharedData
{
string data { get; set; }
}

2.) On the source/provider webpart, perform the following actions :

a.) Implement our data object interface. The below sample returns the value from a text field.
string ISharedData.data
{
get { return txtData.Text; }
set { txtData.Text = value; }
}


b.) Setup a connection point which the consumer object can use. For this, use the ConnectionProvider attribute.
[ConnectionProvider("Sample Shared Data Provider")]
public ISharedData GetCommunicationPoint()
{
return this as ISharedData;
}

3.) On the consumer webpart , access the connection point (and effectively the shared data) by implementing a method which is marked with the ConnectionConsumer attribute.
[ConnectionConsumer("Shared Data Consumer")]
public void InitializeProvider(ISharedData provider)
{
SharedData = provider;
}


Note that the argument of this custom method is set to the shared interface. When MOSS calls this routine (since we have used the ConnectionConsumer attribute), the parameter provider would be set to the source webpart's interface. Save this value in a local variable for later usage.

Do note that the value maintained in the above private variable was available for usage ONLY the PreRender function of the WebPart. Is it because I have not saved it in a session object ?

protected override void OnPreRender(EventArgs e)
{
if (_sharedData != null)
{
/*use the shared data*/
lblData.Text = "Data from other web part = " + _sharedData.data;
}
}

Anyways, the above bits should get you started with inter webpart communication. Once you have deployed the above two webparts, use the design mode of the zone to setup the source and destination connections.

Note that for intra-webzone webpart communication, things appear to get a bit different. I havent tried it, but you would need to start with changing the parent class for the WebPart (mentioned in Part 1) .

Webparts in MOSS 2007 - Part 3

We might have user customisable properties within our webpart which we think could be customised during design time. Here design time means the mode when you add the webpart into the appropriate webzone from the webpart gallery. At this point, all properties which had the WebBrowsable attribute set (check out Part 1) are available for customization.

For simple properties (say integers/string etc), MOSS automatically renders the editors for you. It even figures out the enumerations and renders the appropriate listbox..all good. For properties which need customisation, say you need to list down custom values from a DB, you would need to create a custom editor class separately and map it within the webpart.

Steps
1.) Create the editor object for the webpart

a.) Create a new class deriving from EditorPart class and create any control which you would need to use by overriding the CreateChildControls(), similar to the way we created child controls for WebParts.

b.) Now listen carefully. When we think about the Editor which would be shown, there are two important data handling instances - when the editor is shown the first time, we would need to load the currently set value in the webpart and when the editor value is applied/saved, we need to save the selected value back to the webpart.


To handle these two instances, we need to override the SyncChanges() and ApplyChanges() routines. The WebPart being edited at these points is available using the WebPartToEdit property.

Thats it, the webpart editor is ready. What is remaining is linking this editor with the WebPart.

2.) Link the editor with the WebPart.
Our WebPart needs to implement the IWebEditable interface to tell MOSS that it supports custom editors such that the right editor gets shown when our webpart is selected.

IWebEditable requires one method and one get property to be implemented:

Method CreateEditorParts - Override this method to specify all the editors which you need this web part to use (havent tried with more than one). It is at this routine we add our custom editor which we just created and return the editors list.

Property WebBrowsableObject - This expects you to return the current webpart which is currently being edited. (simplest to implement I guess :) )

Once we have the above two steps done, we are nearly done with the editor. Following is a code snippet for a quick look:


public class CustomEditorWebPart : WebPart, IWebEditable
{
private string _reviewer;

public string Reviewer
{
get { return _reviewer; }
set { _reviewer = value; }
}

#region IWebEditable Members

EditorPartCollection IWebEditable.CreateEditorParts()
{
List<EditorPart> editors = new List<EditorPart>();
editors.Add(new ReviewerEditor());
return new EditorPartCollection(editors);
}

object IWebEditable.WebBrowsableObject
{
get { return this; }
}

#endregion

protected override void Render(HtmlTextWriter writer)
{
writer.Write("Reviewer : " + Reviewer);
}
}

public class ReviewerEditor : EditorPart
{
private ListBox myListBox;

public ReviewerEditor()
{
ID = "ReviewerEditor";
Title = "Edit the Reviewer";
}

protected override void CreateChildControls()
{
myListBox = new ListBox();

myListBox.Items.Add("John");
myListBox.Items.Add("Thomas");
myListBox.Items.Add("Bipin");

Controls.Add(myListBox);
}

public override bool ApplyChanges()
{
EnsureChildControls();
CustomEditorWebPart oPart = (CustomEditorWebPart) WebPartToEdit;
oPart.Reviewer = myListBox.SelectedValue;
return true;
}

public override void SyncChanges()
{
EnsureChildControls();
CustomEditorWebPart oPart = (CustomEditorWebPart) WebPartToEdit;
myListBox.SelectedValue = oPart.Reviewer;
}
}




Next time lets look at inter communication between webparts :)


Webparts in MOSS 2007 - Part 2

In the first part, we talked about creating the simplest of WebPart and the classes of concern. This time, lets render something meaningfull in our webpart. How about the list of all the users in the system ? We would be using the MOSS libraries to get this information.

Steps
As usual, create a blank class derived from System.Web.UI.WebControls.WebParts.WebPart and override the render method as :

protected override void Render(HtmlTextWriter writer)
{
writer.Write(GetHTML());
}

private string GetHTML()
{
string result = "<table border=\"0\">";
try
{
ServerContext context = ServerContext.GetContext(Context);
UserProfileManager profileManager = new UserProfileManager(context);
foreach (UserProfile profile in profileManager)
{
if (profile.PublicUrl.AbsoluteUri != null)
{
result += "<tr><td><a href=\"" + SPEncode.HtmlEncode(profile.PublicUrl.AbsoluteUri) + "\"/>" +
SPEncode.HtmlEncode(profile[PropertyConstants.AccountName].ToString()) + "</a></td></tr>";
}
}
result += "</table>";
}
catch (Exception ex)
{
result += "<tr><td>" + ex.ToString() + "</td></tr></table>";
}
return result;
}


As seen, the GetHTML function returns the HTML string which needs to be rendered at the Render() function. GetHTML uses the server context together with the UserProfileManager to get all the user profiles in the Sharepoint system. In addition to this, we have rendered a link for each of the user such that it takes you to the home page of the user.

You would need to include the following namespaces in the 'using' section if not already done : Microsoft.Office.Server, Microsoft.Office.Server.UserProfiles, Microsoft.SharePoint.Utilities, Microsoft.SharePoint.

Follow the either of the two steps mentioned in the previous post to register this webpart on the server and test it out.

More rendering with data from the DB

Lets create another WebPart which renders data from the DB onto a DataGrid. The core idea remains the same. You perform the render on the items that you know and for your child controls (DataGrid, Label etc), you ask them to render themselves.The crux of the code is contained in the following :

protected override void Render(HtmlTextWriter writer)
{
EnsureChildControls(); //makes sure the child control were created

LoadData();

writer.RenderBeginTag("table");

writer.RenderBeginTag("tr");
writer.RenderBeginTag("td");
lblSubmit.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();

writer.RenderBeginTag("tr");
writer.RenderBeginTag("td");
gridProductList.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();

writer.RenderEndTag(); //end table
}

protected override void CreateChildControls()
{
base.CreateChildControls();

lblSubmit = new Label();
lblSubmit.Text = "Employee List";
Controls.Add(lblSubmit);

gridProductList = new DataGrid();
Controls.Add(gridProductList);
}


Note the usage of RenderBeginTag and RenderEndTag which generates the matching start and end tags. Within each of the tags, we render the specific control. As seen, the DataGrid (gridProductList) gets rendered in the appropriate table column within a html row.

LoadData() function referred in Render() basically loads the data into the DataGrid using standard data access calls. Any child control the webpart uses should desirably be created at the CreateChildControls method. Though we might as well do this when the WebPart gets created, writing it here ensures that EnsureChildControls() call it when required.

I hope this gives you a gist of how WebParts are created and used. In the next parts, we shall see about Custom Editors and inter WebPart communication.

Monday 25 February 2008

Webparts in MOSS 2007 - Part 1

Webparts can be considered as reusable widgets (similar to the yahoo ones) which work independently (though they can communicate) and are individually configurable. This makes the task of showing up of different logical sections on the same web page easier; especially while doing independent development. Brought out initially with Sharepoint 2003, it was supported in ASP.NET extensively and now in MOSS 2007 .

OOB WebParts
Out of the box, MOSS provides you with numerous webparts which could be used with minimal configuration. You would want to check out the webpart gallery which list down these.

Some of the interesting O
OB webparts :
Image Webpart : To display images from sites/from another webpart

Site Aggregator : To display sites of your choice
RSS Viewer : To get feeds from any RSSContent Query : To display a content type.
Business Data List : List from LOB/Webservice configured in BDC

Development Concerns
As soon as you start to swim through webpart documentations, you are faced with the dilemma of two parent WebPart classes which provide more or less the same functionality. Which one do you use? From what I could figure out, System.Web.UI.WebControls.WebParts.WebPart is the most commonly used for WebPart development and the simplest.


The immediate descendant class defined in Microsoft.SharePoint.WebPartPages.WebPart was intentionally provided for compatibility with Sharepoint 2003 and also for performing complex webpart functionalities which include cross page web part communication, communication between webparts not in the same webzone, data caching etc.

The good part is, if you use the System.Web.UI.... WebPart, you could reuse the webpart in an ASP.NET application too, assuming your webpart does not have any MOSS specific calls. To summarise, stick onto the System.Web...WebPart for most of your regular requirements .


Classes of concern
WebPartManager - Microsoft (and now me) loves manager and provider classes. In this case, WebPartManager acts as the point of entry to access the various features of the various webparts in a page. This means that there is exactly one WebPartManager for a webpage. If you are using the masterpage provided by MOSS 2007, this would mean that the WebPartManager is already available to you (things get different when you do plain ASP.NET development)


WebPartZone - This is physical place/zone on the page where WebParts reside.

Writing the first WebPart

Now that we are decided on the parent webpart class to use for our WebPart, the main job we have is to tell what needs to be rendered. Simple enough, override the render function :)

public class MyFirstWebPart : System.Web.UI.WebControls.WebParts.WebPart
{

private string displayText = "MOSS Rocks";

[WebBrowsable(true), Personalizable(true), FriendlyName("Display Text")]
public string DisplayText
{
get { return displayText; }
set { displayText = value; }
}

protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
writer.Write("Typed Text is " + displayText);
}
}


OK, so who is going to talk about the attributes? Here goes:

WebBrowsable - makes sure that this property is listed in the property editor when the WebPart is configured.

Personalizable - Setting a value of true on this attribute makes sure that the property value is maintained for individual users.
FriendlyName - The easiest of the lot, this displays the friendly text for this property in the property editor.

Deploying a WebPart
There are two ways to deploy a webpart to the server :
a.) Direct copy/register Copy the WebPart dll to the _app_bin of your application and mark the assembly as safe in web.config. There you go, WebPart is all setup in your current application and ready to use.



b.) CAB This is the recommended way to deploy webparts in the live environment. What you do is create a DWP (dashboard web part) XML file together with a manifest file, wrap it up in a CAB project. Use this CAB project together with the stsadm command line tool at the server to register your WebPart. Check out more about DWP files here.

In the next post, we shall look at rendering some meaningful stuff at the render method, Property Editors, Inter Communication between WebParts.

Wednesday 28 November 2007

MS Sync Framework

To prove that they are everywhere, they have now brought out a framework just for synchronising stuff across two domains. Based on a provider model, this could be extended to sync any data (files, tables,). Sync services for ADO.NET is one provider which you could readily use to sync data from your client machine to the DB server. The other one readily available being the file services provider.

Check out the classes in Microsoft.Synchronization.Data.SqlServerCe to start with this and also check out the ADO.NET BOL.

SLP Services

Security services in .NET goes to the next level now with the introduction of these services. Obfuscation had until today been one of the most commonly used methods to hide your source code while SLP appears to use a new approach altogether.

The new set of keyword to learn for the day include:

SLP (Software Licensing and Protection) - The service itself.

SVML (Secure Virtual Machine Language) - Similiar to MSIL, bits of code which has been transformed.

SVM (Secure Virtual Machine) - To achieve code transformation, you select parts of the application you would want to secure. What SLP does at this point is to include an SVM with the many transformed SVML's as part of the application. When the assemblies are consumed by the client, these SVML's execute in its own SVM. To complicate things further for our hacker, each software vendor would supposedly get an SVM with a unique permutation. Effectively, the same code transformed by one vendor would not be readable by another vendor. Definitely something to watch out for.

In addition to the code protection, MS appears to have integrated product licensing & feature level activation into this service making it a complete security solution for .NET applications.

Code Protector SDK - You could use this SDK to transform your code into SVML using your custom permutation. Check out the Microsoft.Licensing namespace.

SLP Server - This server application could be used to manage the 'feature' activation through a web service and also perform customisation of packages. Instead of buying (and maintaining) this product, you could instead subscribe to the SLP online service provided by MS.

Monday 15 October 2007

Audience in MOSS

Concept

Audience feature in MOSS should not be mixed up with security features/trimming of data based on the user credentials and the resulting authorization. Instead, 'audience' lets you filter out undesirable data for the current user context. E.g.:- you would want to hide the sales data on the home page if the current user is not part of the sales team; this makes sure the marketing (or other) teams are not overloaded with unwanted information.

In MOSS, the idea is to setup a user/audience list such that webparts/lists can later show/hide the required information. This is supported out of the box.

Setting Up an Audience

From a shared service provider, you could add new audiences by creating rules using windows groups or distribution list or any of the property available against a user (name, address, department, manager etc).

To extend this audience definition, we could have custom property (say 'Day/Night Shift') added against a user from the Shared Services->User Profile settings. This makes sure that we can create rules based on this new custom property to show items specific to employee working in the day shift.

Consuming an Audience

Out of the box, sharepoint lets you apply audience-targeting for the following items

a.) WebParts - while designing the webpart, you could specify the target audience for this webpart. In this case, the webpart would be rendered only if the current-user is part of the target audience. In all other cases, the webpart would not be visible / rendered.

b.) Lists - While designing a list, you could specify that the list needs to have audience targeting available. In this case, you could use Content Query WebPart to filter out data


Consuming Audience programmatically

AudienceManager class - Acts as the entry point into the entire functionality of audience in MOSS. This class also implements the IRuntimeFilter interface to perform the targeting functionalities for webpart/lists.

For a custom user control to consume the Audience functionalities, one of the way is to setup an audience list for the custom control (a new property perhaps) such that at runtime, the control can use AudienceManager.IsCurrentUserInAudienceOf() method to check if the active user is part of the audience previously setup. Note that in this case, it’s up to the control developer to implement the required functionality of hiding/filtering data etc.