SharePoint ASP.Net 3.5 Upgrade Feature

I’ve finally gotten around to starting a CodePlex project for publishing SharePoint features called Trentacular SharePoint 2007 Features. The first contribution to the project should reach a broad audience (within the SharePoint population that is) … an ASP.Net 3.5 Upgrade Feature.

The Feature is naturally scoped to a Web Application and adds ASP.Net 3.5 entries to the particular Web Application’s web.config file. It works for both WSS and MOSS and also supports clean deactivation by iterating through the SPWebConfigModification collection and removing modifications by owner instead of recreating the modifications. All modifications are done through a utility class called SPWebConfigModificationHelper which can be taken advantage of alone if you are writing your own modifications.

Before deciding to write this feature, I tried several others that have been published. Each had its own issues, from not supporting clean deactivation to simply just not adding the correct entries. The entries this feature writes were taken from the Visual Studio ASP.Net 3.5 Web.config template.

If you give this Feature a shot, please let me know how it goes.

Generic Feature Receiver for Features that Deploy WebParts

Greg Galipeau wrote a thorough post on cleaning up your Web Part Gallery that is the basis for this post. To summarize, Greg shares a Feature Receiver he uses as a generic Feature Receiver for all his WebPart Features that simply removes the WebPart having the same name as the Feature’s DisplayName when the Feature is deactivated.

There are a couple of issues with this though:

  1. It assumes that the WebPart has the same name as the Feature
  2. It limits the Feature to deploying just a single WebPart

An alternative approach would be to inspect the Feature’s element definitions and extract the exact WebPart names that were deployed, thus resolving the two issues noted above.

Below is the alternative Feature Receiver (it extends the BaseFeatureReceiver I mentioned in my previous post):

    public class WebPartFeatureReceiver : BaseFeatureReceiver<SPSite>
    {
        public override void FeatureDeactivating(SPSite site, SPFeatureReceiverProperties properties)
        {
            base.FeatureDeactivating(site, properties);
 
            var elements = properties.Definition.GetElementDefinitions(CultureInfo.CurrentCulture);
            var webparts = elements.Cast<SPElementDefinition>()
                .SelectMany(e => e.XmlDefinition.ChildNodes.Cast<XmlElement>()
                    .Where(n => n.Name.Equals("File"))
                    .Select(n => n.Attributes["Url"].Value)
                    )
                .ToList();
 
            var rootWeb = site.RootWeb;
            var wpGallery = rootWeb.Lists["Web Part Gallery"];
 
            var galleryItems = wpGallery.Items.Cast<SPListItem>()
                .Where(li => webparts.Contains(li.File.Name))
                .ToList();
 
            for (int i = galleryItems.Count - 1; i >= 0; i--)
            {
                var item = galleryItems[i];
                item.Delete();
            }
        }
    }

(SPWeb)properties.Feature.Parent no more … a handy Feature Receiver Base Class

If you are writing SharePoint feature receivers often, you will find there are several lines of code common to just about every feature receiver:

  1. A cast of properties.Feature.Parent to the appropriate scope (either SPWeb, SPSite, SPWebApplication, or SPFarm)
  2. Empty implementations of FeatureInstalled and FeatureUninstalling

Provided below is an abstract generic class that now serves as the base class for every feature receiver I write. It eliminates the redundant code by performing the cast for you based on the classes generic type parameter and also goes ahead and implements all the abstract methods, making it so that you to only need to override the actual methods you plan to handle. Your extending feature receiver now will look like the following:

    public class MyFeatureReceiver : BaseFeatureReceiver<SPWeb>
    {
        public override void FeatureDeactivating(SPWeb scope, SPFeatureReceiverProperties properties)
        {
            // Pointless, but what the heck
            base.FeatureDeactivating(scope, properties);
 
            // Now do something with your scope object, in this case an SPWeb
            ...
        }
    }

And now, the code for the BaseFeatureReceiver:

    /// <summary>
    /// Base class that makes the feature scope available as an argument to the
    /// FeatureActivated and FeatureDeactivating methods
    /// </summary>
    /// <typeparam name="T">
    /// T is the class type of the scope of the feature (SPFarm, SPWebApplication, SPSite, or SPWeb)
    /// </typeparam>
    public abstract class BaseFeatureReceiver<T> : SPFeatureReceiver
    {
        public sealed override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            FeatureActivated((T)properties.Feature.Parent, properties);
        }
 
        public virtual void FeatureActivated(T scope, SPFeatureReceiverProperties properties) { }
 
        public sealed override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            FeatureDeactivating((T)properties.Feature.Parent, properties);
        }
 
        public virtual void FeatureDeactivating(T scope, SPFeatureReceiverProperties properties) { }
 
        public override void FeatureInstalled(SPFeatureReceiverProperties properties) { }
        public override void FeatureUninstalling(SPFeatureReceiverProperties properties) { }
    }

Hope you find this helpful.

nHibernate in the Enterprise

I just recently received this response regarding the use of nHibernate within the division of the company I am working with:

… [nHibernate] has alot of promise I think but [coorporate] doesn’t consider it “stable” yet …

Are you kidding me? Where do these guys get their information? I guess the Microsoft Entity Framework is stable enough though.

Lets see, nHibernate is a port of the Java Hibernate Framework which was first released on November 30, 2001 (making it almost 8 years old). The major 1.2.1 version of nHibernate was released in November 2007, improving on the already stable port of the the Hibernate framework, adapting .Net 2.0 language features.

I began using the Hibernate framework in the Fall of 2005 being introduced to it by my colleague Winston Fassett. I switched over to .Net development in the Fall of 2006, and have been using nHibernate from the start.

Having used nHibernate since before the 1.2.1 release, I can safely vouch for its stability and performance … visit http://racenation.com for an example of my most recent nHibernate project.

I can’t say that some of the surrounding projects like Linq to nHibernate, although very cool, can be considered stable, but how long does a framework need to be around and used to be considered stable?

Galleriffic 1.0

*************************************************************

Update: Galleriffic 2.0 has been released. Read about it here.

*************************************************************

At long last, I have released the 1.0 version of Galleriffic, which packs all sorts of new features.

For first time readers, Galleriffic is a jQuery photo gallery optimized to handle high volumes of photos while conserving bandwidth. h0bbel has posted a nice read on some of the benefits of Galleriffic over other photo gallery scripts.

New Features

  • Updated to work with jQuery 1.3 (apparently jQuery 1.3.2 introduces a flicker the gallery advances)
  • OnTransitionIn and OnTransitionOut events that allow for plugging in custom transitions
  • SlideShow auto start option
  • Disable history and url hash writing option
  • Support for showing an element (such as an animated loading image) when waiting for an image to load
  • Thumbnails are now simply shown/hidden instead of being rebuilt which allows for any custom html to be used as a thumbnail
  • Replaced invalid html attributes “description” and “original” with an optional caption element that can contain any custom html
  • Changed the toggle slideshow span to a hyperlink

By exposing both image and page transition events, the doors of creativity are now open for plugging in your own image transitions. This should also lend to a more modular approach for extending Galleriffic.

FAQ

Will Galleriffic generate the slide and thumbnail images automatically?

I have been asked a lot if Galleriffic automatically generates the thumbnails and slides.

Galleriffic by itself does not do any image processiong or generation; however, there is a great tool that does: jAlbum. I have created a jAlbum skin that I use myself for creating my personal galleries. After installing jAlbum and the Galleriffic jAlbum skin, simply choose your source image directory and an output directory, click “Make Album”, and wallah, you now have a complete html gallery with 3 different size versions of each image.

How can I center the slide image?

Thanks to Cristi for providing a solution to this problem. Click here to read her solution.

How can I change the number of thumbnail columns?

With the stylesheet used in the example, each thumbnail is floated left, and thus as many thumbnails that will fit in the width of the column will be displayed. If you want fewer or more columns, make the width of the navigation panel smaller or larger. I am currently setting its width using javascript with the following line in the html:

document.write(<!-- div.navigation{width:300px;float: left;}div.content{display:block;} -->
 
”);

It has been pointed out in the comments that a better way to do this is to use the jQuery.css() method just before initializing the gallery.

Where do I submit my issues or enhancement requests?

I started an issues list that I will start working off of for future releases here. Submit your issues and enhancement requests in this list.

Exposing the State of a SharePoint Workflow Instance

Scenario

I have a fairly straight-forward sequential approval workflow that has an EnableModificationActivity that is in scope for just about the entirety of the workflow. My modification form is an ASPX page that gives the user the ability to enable/disable approval steps that have not occurred yet in the workflow. Since the workflow is able to be modified multiple times, I would like the form to reflect the current state of the workflow, meaning it should show which activities are currently enabled or disabled.

Solution

So here is the approach I ended up taking…

  1. I created a Plain Old CLR Object (POCO) class specific for holding state information my particular workflow that is capable of being XML Serialized. I’ll call this the “State Object” going forward.
  2. I created a reusable class called “WorkflowStateManager” that is capable of loading and saving single State Objects for a given SPWorkflow. This class is accessible by both the workflow and the modification form.
    • Save State Implementation:
      1. XML serializes the object to a string
      2. Sets the serialized string on the SPWorkflow list item’s property bag and calls the property bag’s Update() method
    • Load State Implementation (Essentially the reverse of the Save State implementation)
      1. Gets the serialized string from the SPWorkflow list item’s property bag
      2. XML Deserialize the string into the State Object
  3. When the workflow is activated, I construct a new State Object, initialize various properties on it, and save it using the WorkflowStateManager.
  4. As the workflow progresses, I load and update the State Object as needed in the following manner:
    • Use the WorkflowStateManager to load the current State Object
    • Make workflow decisions based on the State Object’s values
    • Make desired changes to the State Object
    • Use the WorkflowStateManager to save the State Object
  5. Now, my modification form is also able to load, manipulate, and save the State Object using the WorkflowStateManager, and in turn expose the current state of the workflow to the user.

I hope this might be of benefit to someone.

SharePoint Dependency Injection Approach

I just finished delivering a SharePoint workflow application in which I developed what could be called a start to framework that I would like to reuse in my next project. The framework currently has many features such as:

  • An “Object-List Mapping” infrastructure complete with lazy initialization of objects and collections, converters, and caching
  • A list item event broker
  • Several handy web controls
  • Logging
  • State management
  • Plenty of Utility Helpers

Each major feature is defined with an interface, and there is a single context class scoped to an SPWeb that holds references to each feature provider. For this last application, and I imagine I will do this for most applications going forward, I extended the context class to hold additional references to application-specific business logic helpers and data access interfaces.

In order to accomplish dependency injection of the feature providers, there is a context factory interface that is responsible for constructing and initializing contexts. I implemented a static ”default” context factory that when called upon, it first looks in the SPWeb property bag to see if a predefined custom context factory type has been specified. If it finds one, it constructs a new instance of the custom factory and returns the context from the custom factory’s CreateContext method.

    public static class SPAppContextFactory
    {
        public const string CustomSPAppContextFactoryKey = "custom_context_factory_key";
 
        public static void RegisterSPAppContextFactory<T>(this SPWeb web) where T : ISPAppContextFactory
        {
            web.AllProperties[CustomSPAppContextFactoryKey] = typeof(T).AssemblyQualifiedName;
            web.Update();
        }
 
        public static void UnregisterSPAppContextFactory(this SPWeb web)
        {
            if (web.Properties.ContainsKey(CustomSPAppContextFactoryKey))
            {
                web.AllProperties[CustomSPAppContextFactoryKey] = null;
                web.Update();
            }
        }
 
        public static SPAppContext CreateSPAppContext(this SPWeb web)
        {
            // First check Web Properties if an alternate factory is specified
            if (web.AllProperties.ContainsKey(CustomSPAppContextFactoryKey))
            {
                string customFactoryTypeName = web.AllProperties[CustomSPAppContextFactoryKey];
                Type customFactoryType = Type.GetType(customFactoryTypeName, true);
                ISPAppContextFactory customFactory = (ISPAppContextFactory)Activator.CreateInstance(customFactoryType);
                return customFactory.CreateContext(web);
            }
 
            // Create the Default Context
            return new SPAppContext(web);
        }

You can now register your applications custom context factory in the FeatureActivated method of a feature receiver. You’ll need to include the namespace of your static factory class in order to make the extension methods available, and then simply call the RegisterSPAppContextFactory extension method .

        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            var web = (SPWeb)properties.Feature.Parent;
            web.RegisterSPAppContextFactory<MyCustomContextFactoryClass>();
        }
 
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            var web = (SPWeb)properties.Feature.Parent;
            web.UnregisterSPAppContextFactory();
        }

The context factory now serves as a single place where all dependencies are wired up, and the framework is now able to instantiate custom contexts without being aware of any feature provider implementation.

SharePoint Workflow: Getting Replicator ChildData within a CreateTask Activity

Disclaimer: the following code requires version 3.5 of the .Net Framework, but can easily be modified to run using .Net 2.0

Scenario

So I was asked to develop a fairly straight forward approval activity in which several users were to be assigned an approval task in parallel. Microsoft offers its ReplicatorActivity to accomplish just this. The gotcha is that when running the ReplicatorActivity in parallel, the CurrentIndex property of the replicator is always the last index value of its child data. So how do you know which ChildData item the replicator is currently executing upon?

Solution

What I came up with seems a little hacky, but has been working beautifully for me. Here it goes…

The replicator contains a DynamicActivities property which holds references to each of the replicated activities it creates. Using Linq, we can easily convert this collection to a list, and then determine the index of the current activity within our newly converted activity list. The following example shows how I am using this method to access the current child data from within a CreateTask activity:

        private void createTask_Invoked(object sender, EventArgs e)
        {
            var createTask = (CreateTask)sender;
            string approver = (string)WorkflowUtils
                .GetReplicatorChildData(createTask);
 
            ...
 
        }

And the magic happens within our WorkflowUtils helper class:

    public static class WorkflowUtils
    {
        public static Activity GetTopMostReplicatorChild(Activity activity)
        {
            var parent = activity.Parent;
            if (parent == null)
                return null;
 
            if (parent is ReplicatorActivity)
                return activity;
 
            return GetTopMostReplicatorChild(activity.Parent);
        }
 
        public static object GetReplicatorChildData(Activity activity)
        {
            var topMostChild = GetTopMostReplicatorChild(activity);
            var replicator = (ReplicatorActivity)topMostChild.Parent;
 
            int currentIndex = replicator.DynamicActivities
                .ToList()
                .IndexOf(topMostChild);
 
            return replicator.CurrentChildData[currentIndex];
        }
    }

SharePoint Logging and Microsoft’s Trace Log Provider

Microsoft has a posted example of how to log to the Trace Log here.

So how does one use this in their application? The below code is how I plugged it into our SharePoint development framework. I’d love to hear how you are using it.

Step 1:

I modified the Microsoft’s TraceProvider’s RegisterTraceProvider and UnregisterTraceProvider methods as follows:

        private static readonly object RegisterLock = new object();
        private static bool isRegistered = false;
        public static unsafe void RegisterTraceProvider()
        {
            lock (RegisterLock)
            {
                if (!isRegistered)
                {
                    SPFarm farm = SPFarm.Local;
                    Guid traceGuid = farm.TraceSessionGuid;
                    uint result = NativeMethods.RegisterTraceGuids(ControlCallback, null,
                        ref traceGuid, 0, IntPtr.Zero, null, null, out hTraceReg);
                    System.Diagnostics.Debug.Assert(result == NativeMethods.ERROR_SUCCESS);
                    isRegistered = true;
                }
            }
        }
 
        public static void UnregisterTraceProvider()
        {
            lock (RegisterLock)
            {
                if (isRegistered)
                {
                    uint result = NativeMethods.UnregisterTraceGuids(hTraceReg);
                    System.Diagnostics.Debug.Assert(result == NativeMethods.ERROR_SUCCESS);
                    isRegistered = false;
                }
            }
        }

Step 2:

I created our own TraceLogProvider object extending our BaseLogProvider which implements our custom ILogProvider interface. This provider object makes the appropriate calls into Microsoft’s TraceProvider, but now exposes a friendly logging interface to use within our application.

    public class TraceLogProvider : BaseLogProvider, IDisposable
    {
        public string ApplicationName { get; set; }
 
        public void Log(string msg, LogSeverity severity)
        {
            var traceSeverity = GetTraceSeverity(severity);
 
            TraceProvider.RegisterTraceProvider();
 
            string[] lines = msg.Split(new string[] { Environment.NewLine },
                StringSplitOptions.RemoveEmptyEntries);
            foreach (string line in lines)
            {
                TraceProvider.WriteTrace(0, traceSeverity, Guid.Empty, "Your Company Name",
                    ApplicationName, "General", line);
            }
        }
 
        private TraceProvider.TraceSeverity GetTraceSeverity(LogSeverity severity)
        {
            switch (severity)
            {
                case LogSeverity.Info:
                    return TraceProvider.TraceSeverity.InformationEvent;
                case LogSeverity.Warn:
                    return TraceProvider.TraceSeverity.WarningEvent;
                default:
                    return TraceProvider.TraceSeverity.CriticalEvent;
            }
        }
 
        #region IDisposable Members
 
        public virtual void Dispose()
        {
            TraceProvider.UnregisterTraceProvider();
        }
 
        #endregion
    }

And for reference, our BaseLogProvider is shown below:

    public abstract class BaseLogProvider : ILogProvider
    {
        #region ILogProvider Members
 
        public virtual void Info(string msg)
        {
            Log(msg, LogSeverity.Info);
        }
 
        public virtual void Info(Exception e)
        {
            Log(e, LogSeverity.Info);
        }
 
        public virtual void Info(string msg, Exception e)
        {
            Log(msg, e, LogSeverity.Info);
        }
 
        public virtual void Warn(string msg)
        {
            Log(msg, LogSeverity.Warn);
        }
 
        public virtual void Warn(Exception e)
        {
            Log(e, LogSeverity.Warn);
        }
 
        public virtual void Warn(string msg, Exception e)
        {
            Log(msg, e, LogSeverity.Warn);
        }
 
        public virtual void Error(string msg)
        {
            Log(msg, LogSeverity.Error);
        }
 
        public virtual void Error(Exception e)
        {
            Log(e, LogSeverity.Error);
        }
 
        public virtual void Error(string msg, Exception e)
        {
            Log(msg, e, LogSeverity.Error);
        }
 
        public virtual void Log(Exception e, LogSeverity severity)
        {
            Log(FormatException(e), severity);
        }
 
        public virtual void Log(string msg, Exception e, LogSeverity severity)
        {
            Log(msg + "\n" + FormatException(e), severity);
        }
 
        public abstract void Log(string msg, LogSeverity severity);
 
        #endregion
 
        protected virtual string FormatException(Exception ex)
        {
            StringBuilder stringBuilder = new StringBuilder();
 
            stringBuilder.AppendLine(ex.ToString());
            if (ex.InnerException != null)
            {
                stringBuilder.AppendLine(ex.InnerException.ToString());
                if (ex.InnerException.InnerException != null)
                {
                    stringBuilder.AppendLine(ex.InnerException.InnerException.ToString());
                }
            }
 
            return stringBuilder.ToString();
        }
    }
 
    public enum LogSeverity
    {
        Info,
        Warn,
        Error
    }

SharePoint Security Validation and Unsafe Updates

Seeing this? The security validation for this page is invalid. I am sure this error has good intentions, but it is showing its face far too often. Hristo Pavlov has written a good explanation on AllowUnsafeUpdates here.

If you read my previous post and are attempting to update list items as the System User, you will likely run into this problem. Setting AllowUnsafeUpdates to true for the duration of the update has allowed me to get around this exception. Here is the helper method I am using to accomplish this:

        public static void DoUnsafeUpdate(SPWeb web, Action action)
        {
            bool allowUnsafeUpdates = web.AllowUnsafeUpdates;
            web.AllowUnsafeUpdates = true;
            action();
            web.AllowUnsafeUpdates = allowUnsafeUpdates;
        }

Couple this with the helper methods in my previous post, and you now can do both together:

        public static void DoUnsafeUpdateAsSystemUser(SPWeb web, SPWebAction action)
        {
            DoAsSystemUser(web, delegate(SPWeb systemWeb)
            {
                DoUnsafeUpdate(systemWeb, delegate()
                {
                    action(systemWeb);
                });
            });
        }