Using Reflection to set web part properties

Think about this scenario: you are tasked to do develop a custom SharePoint web part with a few custom properties. This custom web part will be removed and added back to a page during feature activation during a typical solution patch deployment process.
The requirement dictates that web part properties are preserved instead of being reset to their default value after this patch is deployed.

I tried to make a general solution to this by not worrying about the Type of the web part of which the properties we want to preserve. This makes use of Reflection in order to retrieve and set the web part properties. When I implement my web parts, I typically group custom properties in category groups by decorating the property with System.ComponentModel.CategoryAttribute attribute.

For example:

[System.Web.UI.WebControls.WebParts.WebBrowsable(true),
System.ComponentModel.Category("Parameters"),
Microsoft.SharePoint.WebPartPages.FriendlyName("Header Text"),
System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
System.Web.UI.WebControls.WebParts.WebDisplayName("Header Text"),
System.Web.UI.WebControls.WebParts.WebDescription("Header text for the grid")]
public string HeaderText { get; set; }

Right before you remove the old web part, use the following method to collect properties using reflection:

private Dictionary<string, object> CollectExistingProperties(System.Web.UI.WebControls.WebParts.WebPart existingWp)
{
    Dictionary<string, object> existingProperties = new Dictionary<string, object>();
    PropertyInfo[] properties = existingWp.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
    if (properties == null) return existingProperties;
    foreach (PropertyInfo prop in properties)
    {
        object[] myAttributes = prop.GetCustomAttributes(typeof(System.ComponentModel.CategoryAttribute), true);
        if (myAttributes != null && myAttributes.Length > 0)
        {
            for (int j = 0; j < myAttributes.Length; j++)
            {
                if (string.Compare(((System.ComponentModel.CategoryAttribute)myAttributes[j]).Category, "Parameters", true) == 0)
                {
                    existingProperties.Add(prop.Name, prop.GetValue(existingWp, null));
                }
            }
        }
    }
    return existingProperties;
}

Then, for the new web part, use the following method to set custom properties which have been ‘preserved’ earlier:

private void SetPropertyCollection(ref System.Web.UI.WebControls.WebParts.WebPart webPart, Dictionary<string, object> preserved)
{
    PropertyInfo[] runtimeProperties = webPart.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
    if (preserved != null && preserved.Count > 0 && runtimeProperties == null)
    {
        //Handle error
    }

    foreach (PropertyInfo prop in runtimeProperties)
    {
        object[] myAttributes = prop.GetCustomAttributes(typeof(System.ComponentModel.CategoryAttribute), true);
        if (myAttributes != null && myAttributes.Length > 0)
        {
            for (int j = 0; j < myAttributes.Length; j++)
            {
                if (string.Compare(((System.ComponentModel.CategoryAttribute)myAttributes[j]).Category, "Parameters", true) == 0)
                {
                    if (preserved.ContainsKey(prop.Name))
                    {
                        prop.SetValue(webPart, preserved[prop.Name], null);
                    }
                }
            }
        }
    }
}
Advertisements

Powershell script to create event source

Writing entries to application event log is a fairly common routine, and one of the thing to note is that the event source might not already exist and the user account under which your code runs might not have the necessary permissions to create them, which is completely acceptable and in fact, recommended.

Thus, a common practice is to create the necessary event sources upfront during deployment. This can be done using the folloing powershell script:

$eventSources = @(
"Department.Solution.Common",
"Department.Solution.Common.SPHelper",
"Department.Solution.FeatureReceivers",
"Department.Solution.WebControls",
"Department.Solution.WebParts",
"Department.Solution.EventReceivers",
"Department.Solution.Services"
)

foreach($source in $eventSources) {
    write-host "Creating event source $source"
    if ([System.Diagnostics.EventLog]::SourceExists($source) -eq $false) {
        [System.Diagnostics.EventLog]::CreateEventSource($source, "Application")
		write-host -foregroundcolor green "Event source $source created" 
    }
    else
    {
        write-host -foregroundcolor yellow "Warning: Event source $source already exists" 
    }
}

When SPSecurityTrimmedControl AuthenticationRestrictions FAILs, simple workaround

SPSecurityTrimmedControl is a nice offering by Microsoft when it comes showing and hiding parts of your page/web part/control depending upon user permissions.

In my case, I have the requirement to show some parts of my page only to anonymous users, and some only to logged in users, hence giving the illusion of different context for logged on and logged out users while staying on the same page. I was ready to take advantage of the AuthenticationRestrictions property until I found out that it doesnt work when I give it the value of AnonymousUsersOnly

This blog entry by Waldek Mastykarz mentioned this same problem. I figured that the SPSecurityTrimmedControl class is not sealed, yeeha… that means I can extend it to fix this broken feature. I work around this by adding my own property to the extension of SPSecurityTrimmedControl class, and overriding the Render method of this class.

Simply add this class to your project:

[ParseChildren(false), Designer(typeof(SPControlDesigner)), SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true), AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal), SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class SecurityTrimmer : SPSecurityTrimmedControl
{
    private bool _AnonymousOnly = false;

    public bool AnonymousOnly
    {
        get { return _AnonymousOnly; }
        set { _AnonymousOnly = value; }
    }

    [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
    protected override void Render(HtmlTextWriter output)
    {
        if ((!AnonymousOnly && HttpContext.Current.Request.IsAuthenticated) || (AnonymousOnly && !HttpContext.Current.Request.IsAuthenticated))
        {
            base.Render(output);
        }
    }
}

Then I use this within my page by replacing the SPSecurityTrimmedControl with my own control:

<%@ Register TagPrefix="myuc" Namespace="[[Your Namespace here]]" Assembly="[[Your Assembly Name here]]"%>

<!-- your other markup -->

<myuc:SecurityTrimmer id="SecurityTrimmedControl1" runat="server" AnonymousOnly="True">
   Text visible only to anonymous users
</myuc:SecurityTrimmer>

And Voila, it works!

Cannot create SharePoint document workspace from Word

When we create a document and go “Create Document workspace”, sometimes we are told that we are Offline. This can usually happen if we have SharePoint and Office installed within the same box, typically I hit this in development VM.

What to do? Turn out that this is a known issue with SharePoint. To get around this:

  1. Go to Administrative Tools > Services
  2. Stop the System Event Notification service

And voila! Now we’re online!

Awesome SharePoint whitepapers

An awesome lot of Sharepoint whitepapers and demos.

Whitepapers:

  • E-Mail Records Managementin Microsoft Office SharePointServer 2007
  • How to scale out a SharePoint farm and configure IIS 7 Microsoft Network Load Balancing on windows server 2008
  • How to Configure Stress Test Projects for SharePoint 2007 using Visual Studio Team Suite 2008M
  • How to move MOSS2007 from W2003_32bit to W2008_64bit
  • How to move the Shared Service Provider Search database
  • How to configure Email Enabled Lists in Moss2007 RTM using Exchange 2007
  • Securing Central Administration in SharePoint 2007
  • Code Access Security in SharePoint 2007 for Administrators
  • Modify Alert Notifications using AlertTemplates.xml in SharePoint 2007

Demos:

  • Gradual Upgrade of SPS 2003 Medium Farm to 2007 RTM
  • Resetting customized sites and configuring search after an Upgrade to 2007 RTM

Check this out: http://www.combined-knowledge.com/Downloads%202007.htm

Enabling IntelliSense for BDC metadata

BDC metadata authoring can be a daunting task especially when we are so get used toVisual Studio’s IntelliSense. We can actually enable IntelliSense through these steps (in this case I’m using Visual Studio as my XML editor):

1. Open up the XML and click on the ellipsis in the Schemas property (bring up the property window)

2. XML Schemas window will open, click on Add

3. Navigate to BdcMetadata.xsd file, usually this file sits in: Program Files\Microsoft Office Servers\12.0\Bin  folder

If you are authoring BDC metadata outside of Sharepoint Dev box, copy the xsd file out and refer to the location where you copied the file into. In my case, I like to leave my Sharepoint dev VPC running while doing some development on my Vista physical machine, so I copied the BdcMetadata.xsd file out of the VPC and put it on my physical machine.

Silverlight-enabled Service

I was trying to add a service reference from my silverlight project when I encountered several weird 404 and 405 errors.

I finally found out that WCF might not be properly installed on my machine with IIS7, to solve this:

  1. Go to C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\
  2. Run “ServiceModelReg -i” from command prompt as an admin

Problem when importing Excel spreadsheet to Sharepoint List

I came to the following error message when trying to import a shreadsheet into Sharepoint List:

Method ‘Post’ of object ‘IOWSPostData’ failed

I found a post that has the solution for this.

This error is caused by an Excel add in that use an old version of post method which was there in Sharepoint Team Services 1.0. To solve this, we need to direct the excel add-in to communicate with Sharepoint using SOAP through these steps:

  1. Open up C:\Program Files\Microsoft Office\Office12\1033\EXPTOOWS.XLA
  2. Bring up the VBA window using Alt+F11
  3. Find the Initialize() method
  4. Comment out the following line:
    lVer = Application.SharePointVersion(URL)
  5. Put in the following line:
    lVer = 2
  6. Save the Excel add in and retry your import

Very cool Wii remote utilization

http://www.cs.cmu.edu/~johnny/projects/wii/

%d bloggers like this: