Thursday, July 31, 2008

Life is what happens to you when you are busy making plans

"Life is what happens to you when you are busy making plans" - something I stole off a friend's gtalk title. This I believe defines Symphony of life we live. Numerous occasions, it so happens, you anxiously wait for something to come by and make future plans based on it. Turns out, when it's in your grasp, life plays you like a toy and everything you prepared for, looses relevance.
This not only applies to one's personal schedule, but generates a ripple effect, bringing in unplanned change in peoples lives. This unplanned change which influence people directly or indirectly is what I call "controlled chaos". At times, I get amazed as I see, how changes in my life, make other people alter their plans. By people, I not only mean people who are close to me, but also those who I do not interact with in day-to-day basis.
Lets just say, I recent time, I planned out something for myself. From no where came 'a change' which forced me to deviate from my well planned activity and alter my path. Am at my wits end, when I start to think, would have I walked on planned path all the way if that change haven't come by, or has it given me an excuse to console myself with.
One of my close friend, had an accident recently (I pray he recuperate soon). Call it chance, it so happens, the day he received offer letter from his favorite company, the very same night it happened. He was going through a rough phase and was in need of that job desperately. My entire group, was on the hunt for him. Seems to me fate had other plans. Its ironic to see, how a sudden change can make your life go topsy-turvy .

DataForm WebPart [escape from hardcoded List GUIDs]

SharePoint Designer is undoubtedly, an impeccable tool when it comes to quick and dirty UI creation. Quick drag and drop approach of Lists/Libraries allow easy readable access at UI level. However it has certain limitations. For say, we created a new page and added new DataForm webpart using drag and drop in our development environment. Once our development is complete, it is required to move changes to staging environment. So, as a developer I have numerous options to achieve this by using feature deployment, wsp deployment or simply move aspx page to target Site Collection. Doing so, I see my page containing DataForm webpart does not work.

This is due to the fact that, by default, SharePointDesigner binds the control to the list instance using the list instance GUID. To resolve this we need to replace the GUIDs by the list name. The steps to do this are:
1) On the attributes of the DataFormWebPart element replace the attribute ListName="{GUID}" by ListName="LIST_NAME" where LIST_NAME is the name of the list that you are binding to.

2) Go through all of the DataFormParameter elements and replace:

<WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="{GUID}"/>

With:

<WebPartPages:DataFormParameter Name="ListName" ParameterKey="ListName" PropertyName="ParameterValues" DefaultValue="LIST_NAME"/>

3) Go to the ParameterBindings element and replace

<ParameterBinding Name="ListID" Location="None" DefaultValue="{GUID}"/>

With:

<ParameterBinding Name="ListName" Location="None" DefaultValue="LIST_NAME"/>

This should give you a GUID free DataForm webpart that can be placed on a page layout used by multiple sites on your site collection (as long as the name of the list is the same on all sites).

Cheers

Disposing SharePoint site and web objects

There are several different ways of retrieving an SPWeb object.

Take the following 3 examples:

1) SPContext
SPWeb web = SPContext.Current.Web

2) OpenWeb
SPSite site = new Site("http://myserver/");
SPWeb web = site.OpenWeb("");

3) Site "RootWeb"
SPSite site = new Site("http://myserver/");
SPWeb web = site.RootWeb;

Thumb rule:

  • Never dispose SPContext.Current.Web or SPSite.RootWeb
  • Make sure you dispose of [OpenWeb("")] objects and [new Site("")/new Web("")] objects
SPContext is in use by all resident controls and parts on the site and the page. If you dispose of the SPContext.Current.Web object then you will find that you get a nasty error message next time you try to access that object (forcing you to refresh the page). So if you've ever seen the error message below, check your disposal!
As for
[OpenWeb("")] and [new Site("")/new Web("")]objects, if not disposed, applies heavy penalty on system memory.

Hope this helps.

Cheers

Customising Sharepoint Alerts (SPAlert)

Have you ever wanted to modify "Alert" Setting in SharePoint 2007 based on certain criteria. We had a requirement recently, from a client, to modify alert filters in a very specific way. They wanted to change not the functioning but the filters, and they wanted the filters to be generated at run time based on the content type.

Alerts[Info: MSDN SPAlert] can be created based on context of a SPWeb or of SPUser. Take a look at the snipped below.

From SharePoint User
SPAlert user = //fetch SPUser
SPAlert alert = user.Alerts.Add();

From SharePoint Web
SPWeb web = //fetch web
SPAlert alert = web.Alerts.Add();

Bare in mind alerts are user/group centric. When an alerts is created based on web alert object does not have user associated to it. It is imperative that we supply SPUser object. Code below does exactly that.

alert.User = user;

There are other properties one can associate with alters, such as,

* frequency of alters
* alert template
* alert type [based on List or ListItem]
  • Custom
  • List
  • Item
* event to which the alert applies
  • Add
  • All
  • Delete
  • Discussion
  • Modify
and many more.

Filters is one tricky part when comes CAML. CAML can easily be generated using tools such as

* CAML Viewer [Links]
* CAML builder [Links]

CAML generated by these tools include "Query" and "Where" XML element tags, which in my opinion makes perfect sense. As it turns out these tags do not play well when passed in to alert.Filters properties. Remove these tags and pass the inner content and it works like a charm.
Take a look at the code below.

alert.Filter = "<Eq><FieldRef Name=\"ContentType\"/><Value type=\"string\">" + "Item" + "</Value></Eq>";

What we are doing here is applying filters on Field Type "ContentType" having value "Item".

Code Dump
[Code]
SPSite site = null;
SPWeb web = null;
try
{
site = new SPSite("http://server/");
web = site.RootWeb;
web.AllowUnsafeUpdates = true;
SPList list = web.Lists["TestList"];
foreach (SPUser user in web.SiteUsers)
{
if (user.Name.ToUpper().Contains("user name"))
{
SPAlert alert = user.Alerts.Add();
alert.Filter = "<Eq><FieldRef Name=\"ContentType\"/><Value type=\"string\">" + "Item" + "</Value></Eq>";
alert.Title = "My Alert";
alert.AlertType = SPAlertType.List;
alert.EventType = SPEventType.All;
alert.List = list;
alert.AlertFrequency = SPAlertFrequency.Immediate;
alert.AlwaysNotify = true;
alert.Update(true);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("================");
Console.WriteLine(ex.Source);
Console.WriteLine("================");
Console.WriteLine(ex.StackTrace);
}
finally
{
if (site != null)
site.Dispose();
if (web != null)
web.Dispose();
}
[/Code]

Make sure you dispose of site & web objects, if not a heavy penatly will be applied on memory usage.
Alternatively, if you are using SPContext to get site and web object do not dispose then

Hope this helps.

Cheers

Wednesday, July 30, 2008

Custom WebPart connections and Custom WebPart Properties

Custom Properties does not seem to work any longer the way described in article below. Seems like new patches and SP's from Microsoft makes certain changes. Try inheriting webpart from Microsoft.SharePoint.WebPartPages.WebPart.
Note: Do not forget to include System.Web.UI.WebControls.WebParts in your using statements.
Updated properties are as follows:
[WebBrowsable(true)]
[Personalizable(false)]
[WebPartStorage(Storage.Shared)]
[WebDisplayName("User Name(Domain\\username)")]
[WebDescription("User to display in the WebPart.")]
[SPWebCategoryName("Options")]
==================================
I want to share some of my finding on Custom WebPart connections and Custom WebPart Properties. Attached image, shows 2 custom web parts, communicating via a custom web part connection. Additionally, it shows grouping of custom controls under "Custom Properties" label.

Creating a custom WebPart connection is quiet easy. Simply follow the steps below:

a) Create an interface IStringConnection

public interface IStringConnection
{
string ProvidedString { get; }
}

b) Implement System.Web.UI.WebControls.WebParts.WebPart and IStringConnection in connection provider webpart

[ConnectionProvider("String Provider")]
public IStringConnection ConnectionInterface()
{
return this;
}

public string ProvidedString
{
get { return m_string; }
}

c) On connection receiver webpart add following code

IStringConnection m_providerPart = null;

[ConnectionConsumer("String Consumer")]
public void GetConnectionInterface(IStringConnection providerPart)
{
m_providerPart = providerPart;
}

=> However, this technique will not allow you to connect to default/out-of-box lists and libraries. For that, one needs to implement IwebPartField, IWebPartRow, and IWebPartTable on receiver webpart.

That's about it.

Snippet below adds a textbox to Web Part propery view.

// Assign the default value.
[DefaultValue(c_MyStringDefault)]

// Property is available in both Personalization
// and Customization mode.
[WebPartStorage(Storage.Personal)]

// The caption that appears in the property sheet.
[FriendlyNameAttribute("Custom String")]

// The tool tip that appears when pausing the mouse pointer over the friendly name in the property pane.
[Description("Type a string value.")]

// Display the property in the property pane.
[Browsable(true)]

[XmlElement(ElementName="MyString")]
// The accessor for this property.
public string MyString
{
get
{
return _myString;
}
set
{
_myString = value;
}
}

Hope this helps!!






Cheers