Saturday, March 31, 2012

SharePoint 2010 - upgrading site definitions and web templates - part 1

This has been sort of a recurring issue in SharePoint since the time of MOSS 2007 (or rather SharePoint 2003). Not that you can't really upgrade an existing site definition or the new feature based web templates. All new sites that you create based on the site definitions/web templates will reflect the latest upgrades, the real problem is with the sites created before the upgrade. The problem is that those existing sites will remain exactly the same as before the upgrade of the site definition or web template that were once used to create them. In SharePoint 2010 this issue is almost half solved with the introduction of the powerful and more than useful new functionality - the possibility and support for upgrading features. Actually in many cases this can be perfectly sufficient to solve the problem with the upgrade of site definitions and web templates for existing sites. The only thing you need to do is to pack all functionality of your site template in a small set of features and when you want to add some additional functionality you simply add it to one of the existing features in this group. Then you just upgrade the feature in question and you propagate this piece of functionality to the existing sites based on this site template.

This is no doubt a working approach that can be utilized in real life scenarios. Still, it has the flaw that it defeats one of the main purposes of the SharePoint features in relation to SharePoint provisioning which is namely that features are supposed for the greater part to be reusable blocks of functionality. Of course a good share of the features that you create and use will be tailor-made for a specific site definition and won't be intended for being reused in other site definitions but the idea that you always should use tailor-made features doesn't sound good at all. For instance if you create a small custom feature that adds some web analytics support to your sites and intend for it to be reused in many site definitions, the natural approach would be to just extend these site definitions and add the feature to their CAML schema, rather than copy-pasting the feature's code to one of the existing features in these site definitions.

And just to add a small but very important detail to the idea of the SharePoint features as being small reusable blocks of functionality - this is the concept of feature properties. The feature properties allow you to customize the functionality that the feature adds to the target sites, so the outcome of the activation of a specific feature may in many cases depend on the specific context provided by the feature properties.

I have already mentioned several times in a few previous postings of mine that I look at the site definitions and web templates in SharePoint as mere groupings or sets of features. The availability of feature properties extends this idea somewhat, because the site definitions and web templates not only activate the set of features from their schema in the newly created sites but can use one and the same common features in different ways customizing the functionality that the latter add. Actually the ability to activate features with properties is legally available only declaratively in the onet.xml schema files of the site definitions and web templates, you can't do that using code and the object model (unless you use some trickery with reflection). I will mention one other way to achieve that in a little while.

Here we come close to the subject of the pros and cons of using site definitions and web templates as a provisioning approach in SharePoint. Many SharePoint professionals would even recommend against using these in favor of activating the desired features to the target sites manually. In my opinion the site definitions and web templates add a well justified level of abstraction on top of the SharePoint features, the two levels can be seen as the features being the basic blocks of functionality and the site definitions/web templates as sets of features (basic blocks) adding a level of parametrization (by means of feature properties) that creates a complete set of functionality (the sites that you create based on the site definition/web template).

Let's now go back to the main subject of this posting that I mentioned briefly in the beginning. The problem is that we have our site definitions and web templates, which are nice groupings of SharePoint features but SharePoint itself gives us no support for upgrading these "groupings", when we want that for existing sites. Of course, we can automate a lot of stuff in SharePoint 2010 (and with some limitations in SharePoint 2007) using PowerShell but still the best thing you will get out of it is to start activating the new features of your site definition to the old sites that you created based on it before you modified the site definition itself. And I wanted to find a solution to that which was neater and more self-contained. First I defined the task that I wanted to solve - and in the way that I defined it, it didn't look very hard to accomplish - I wanted to only activate the new features of the site definition/web template automatically to the sites that were created with this site definition/web template, before these new features were added to it. And what about the case when we want to make changes to an existing feature in the site definition. Well, it is simple - this is so to say not a problem of the site definition/web template, because there is a dedicated built-in mechanism for that in SharePoint 2010 - the feature upgrade capability. So if you want to modify an existing feature you should do that using the built-in approach for that, and this is basically outside the scope of the site definition/web template upgrade issue (with the concept of the site definition as a grouping, we only care of what features we have in the group, that is which new features we add, I didn't even consider the case of removing features from the site definition).

So, with the clear idea of what I wanted to do, I split the problem in several smaller tasks - the first three were these (the others will be covered in the second part of this posting):
  • first we have to have a way to identify the web template which was used to create the target site - unfortunately SharePoint doesn't provide an OOB way to check this. For site templates this is an easy and trivial job with the help of the SPWeb.WebTemplate and SPWeb.Configuration properties.
  • secondly, a way to get the current/latest version of the onet.xml file of the site definition or web template. We will need this to get the set of features and compare it to the set of already activated features in the target site or sites
  • thirdly, a way to activate feature with feature properties. Remember that using code with the SharePoint object model we can only activate features without properties, the methods that allow activating features with properties are internal and can be accessed only with reflection (and I don't normally like to resort to reflection).
The solving of these tasks was not very difficult to start with and you can download the solution project from here. It contains three main classes, each of which solves one of the tasks mentioned above - given in the same order: WTMarkFeatureReceiver, WebTemplateHelper, FeatureHelper.
Let me now give you some quick and brief details about the way I solved these tasks.

Starting with the first one - just to mention, that it is a bit of a pity that SharePoint doesn't help us much here and we need a custom solution. The good news is that it is fairly easy to achieve that - the main idea comes from this excellent posting about web templates in SharePoint 2010 by Vesa Juvonen. The idea is to save the name of the web template in a the SPWeb.Properties property bag - whose purpose is exactly that - to store any custom data associated with the given SPWeb. Vesa achieves this by using the new PropertyBag feature element in one of the features of his sample web template (check the posting to see exactly how). I wanted to have this approach more readily reusable, so I wrapped it up in a reusable feature, which uses feature properties (no wonder). The feature has a custom feature receiver which is the aforementioned WTMarkFeatureReceiver class - you can check the code in the solution project. What it does is to save several important pieces of information related to the web template in the SPWeb.Properties properties bag property - these are the Guid of the parent feature of the web template, the name of the web template and the version of the parent feature - these are saved with the following keys in the property bag: WebTemplateFeatureId, WebTemplateName and WebTemplateFeatureVersion - basically all you need to identify the web template used to provision the target site.

And here is how you use the "marking" feature in the onet.xml file of your web template:

<Feature ID="48681fc1-97e0-4c9c-8061-c90890aeb64b">
  <Properties xmlns="http://schemas.microsoft.com/sharepoint/">
    <Property Key="WebTemplateFeatureId" Value="$SharePoint.Feature.Id$" />
    <Property Key="WebTemplateName" Value="$SharePoint.ProjectItem.Name$" />
  Properties>
Feature>
 

Note that the feature ID is of a sample feature that I used on my development server. You need to create your own web scoped feature specifying the WTMarkFeatureReceiver class as the feature receiver class for it (the feature can be in both a Farm or a Sandbox solution, the feature receiver works in both cases). Except for the feature ID, which you will have replaced, you don't need to change any other bit in this snippet. It cleverly makes use of two Visual Studio 2010 replace tokens which allow you to easily pass the feature ID of the parent feature of the web template (in whose onet.xml you have placed the snippet) and also the name of the web template itself - which is actually the name of the feature element that contains the web template's onet.xml file. So, you can easily copy-paste this snippet to all your web templates without having to manually enter the parent features' Guid-s and the web templates' names. Pretty neat, isn't it.

As regards the third task (let me jump ahead a bit, I will come back later to the second task) - if you have followed my recent postings you may have noticed that I had a dedicated posting on this subject, which was titled "SharePoint 2010–activate features with feature properties". It achieves this by ... using the SharePoint deployment API (the classes in the Microsoft.SharePoint.Deployment namespace) - maybe something that you wouldn't normally associate with feature activation. Check the posting itself for more details. The bottom line is that this gives you a valid approach for activating features with properties without having to resort to reflection.

And finally ... to the second task, which is solved with the WebTemplateHelper class in the solution project. This class provides a public method to determine which is the parent web template of a give SharePoint site (or the parent site definition if a site definition and not a web template was used to create the target site). Note that this method works only for sites which were "marked" with the custom "marking" feature mentioned above. The class also contains the implementation for retrieving and parsing the onet.xml file of the parent site definition or web template (whichever was used for the specified SharePoint site). And it also contains a public method which actually "upgrades" the target site to the latest version of its parent site definition or web template (only activating the features that are present in the latest onet.xml and not still activated in the specified site):

public static void UpdateSiteToLatestOnet (SPWeb web, bool updateIfWebTemplateOnly, bool updateWebFeaturesOnly)
 
You can see above the declaration of the site definition/web template "upgrading" method for an existing site. You can see that it uses only three parameters - the first one is the target SPWeb instance, the second one - specifies whether to upgrade the site only in case it is a "marked" web template based site (the "non-marked" web template based sites will have their SPWeb.WebTemplate and SPWeb.Configuration properties set, so they will be indistinguishable from sites created with the web template's base site definition). The third parameter specifies whether you want to activate only the missing web scoped features or also the site scoped features when your site is the root site of its site collection.

The code of the class is very brief and easy to follow (at least I hope so), so you can check it for further details.

In the second part of this posting I will continue with explaining how this custom web template upgrading approach can be coupled with the built-in feature upgrade capabilities in SharePoint 2010. Stay tuned.

Friday, January 20, 2012

SharePoint WCM HTML clean-up

SharePoint is a great web content management system, it is fast, scalable, reliable, comes with lots of out-of-the-box components, web parts, etc. that often make the life of the content managers much easier. There are certain aspects of the WCM capabilities of SharePoint though that sometimes need a little more time or some hacking to get them to work properly, or the way you may want them to work. One such thing is the HTML code that appears on your SharePoint pages - there are several things in the HTML generated by the SharePoint WCM system which make it look not quite neat and tidy. The SharePoint UI is built on top of the asp.net Web Forms technology, so SharePoint actually inherits some of the HTML issues directly from its asp.net foundation. The problem here is that when you use asp.net and Web Forms you don't have full control over the HTML that is going to be generated in your page. With the advent of the asp.net MVC this was one of the arguments in favor of the latter, because with asp.net MVC the developer indeed has full control over the generated HTML code. Unfortunately SharePoint doesn't utilize the MVC framework, so many of us at one or another point have had to struggle with the extra HTML bits that get injected in the SharePoint aspx page. Some examples for such bits that come directly from asp.net are the many system hidden fields that appear in the "form" element, the infamous "ViewState" field among them which can grow very big in size, the inline JavaScript blocks with "form" submit helpers, etc. Several intrinsically SharePoint items that further inflate your HTML are for instance the inclusion of the two "core" files: "core.js" and "core.css" (quite big both of them), the many nested HTML "table" elements around your web parts which are rendered by the containing WebPartZone controls, especially in cases when you want your HTML to contain only nice looking "div" elements, etc. The dilemma here is that because SharePoint utilizes in-place page content editing and it is a single aspx file that handles both the editing process and the actual displaying of the page to the end user, the items (web controls in most cases) responsible for these extra (but necessary) HTML artifacts cannot be removed directly from the page. So, we need them for the page content editing, but on the other hand we need to somehow get rid of them, or hide them, or at least suppress the extra HTML that they generate so that the page in display mode shows only the bare minimum of HTML that needs to render the page contents. In the WCM context, I assume here that the SharePoint site is publicly accessible or at least allows anonymous access within some internal network, so the hiding of the extra artifacts is necessary only when the pages are being accessed anonymously. This is a pretty broad scenario and this particular setup is quite popular in the WCM function of SharePoint. The next question is how many of the "extra" HTML SharePoint artifacts may be unwanted in your scenario. If it is about simple content pages with SharePoint field controls only or standard content editor web parts you actually won't need any of the above mentioned bits in display mode with anonymous access. This is especially true when your HTML design is very different from the standard SharePoint page design.
So, after several years and several partial solutions I decided to wrap up the whole thing in a single solution. And it turned out that the solution was pretty easy and simple to develop, and luckily - very easy to use too. It is actually a single user control that you need to place in one place only in your master page. And that's all. The control has several public properties that can be used to configure it, so that it suppresses some of the SharePoint artifacts that it can handle but not others (I will explain these in detail shortly). I chose to create the control as a user control (and there is no code behind assembly, the code is placed inline in the ascx file directly) because this way you have the two deployment options - to either place it in the TEMPLATE/CONTROLTEMPLATES folder of the SharePoint hive, or to upload it to the Master Page Gallery of your site and reference it in your master page from there (I explained this technique in this recent posting of mine). Of course, the code can be easily transferred to a simple web control and put in an assembly of yours.
You can download the user control that I named appropriately "HideSPArtifacts.ascx" from here.
Provided that you have uploaded it to the Master Page Gallery of your site collection (/_catalogs/masterpage) you will need the following lines of code to place it in your master page file:
First you need the "Register" directive at the top of your master page

<%@ Register="" TagPrefix="MyControls" TagName="HideSPArtifacts" Src="~SiteCollection/_catalogs/masterpage/HideSPArtifacts.ascx" %>


The second bit is to place the control declaration in the page mark-up:

<MyControls:HideSPArtifacts runat="server" RemoveCoreJS="false" RemoveCoreCss="false" RemoveHeadCss="false" RemoveForm="false" AddBodyOnLoadDummy="false" EnablePageViewState="true" RemoveZoneHeaders="false"/>


Two very important notes here: 1) if you upload the user control to the Master Page Gallery of your site collection you will have to make additionally certain modifications to your web.config file (check the previous posting that I mentioned above). 2) You need to place the control's declaration (MyControls:HideSPArtifacts) immediately after the opening "html" element of your page and before the "head" HTML element.
One other thing that you should check in your master page is whether you have a "head" element and whether it has the runat="server" attribute (if you have used one of the SharePoint master pages as a base for your master page you will have these). If this condition is not satisfied the control won't be able to remove some of the SharePoint artifacts from the page.
So, after you have the user control in your master page and open a page from your site anonymously (if you view the page as authenticated user the control will do nothing) and you have the values of the control's properties as they are in the snippet above you will see ... no changes in the HTML code of the page. This is because all "Remove.." properties are set to "false". I will now give you a list with the properties of the control and will briefly explain what changes in the generated HTML you will see after setting each property:
  • RemoveCoreJS - as the name suggests, if this property is set, the include script declaration for the SharePoint's "core.js" file is removed from the page
  • RemoveCoreCss - when set, this property causes the core.css style sheet include to be removed from the page. Note that if you use alternate style sheets, these won't be rendered either. This is because the HideSPArtifacts control will block the rendering of the standard SharePoint CssLink control (if available).
  • RemoveHeadCss - when you use certain web controls like the TreeView control, the AspMenu control and some other controls, the asp.net page generates an inline CSS block in its "head" HTML element. If you don't want this inline style sheet to appear in the page (check if this doesn't affect any of the controls that you use on the page), set this property to true.
  • RemoveZoneHeaders - the WebPartZone controls that contain your web parts have the bad habit of creating several nested "table" HTML elements. The web parts' chrome (frame) which you most often set to "none", because you don't need it in WCM public sites also renders a "table" element. If you don't want any of these "table" elements and want to have only the HTML markup directly rendered by your web parts set this property to true. Note that even if you set the "ChromeType" property of the web part, no chrome will be actually rendered (in anonymous mode only).
  • EnablePageViewState - the default value of this property is true and in this case the control will change nothing on the page. If you set this property to false it will simply set the EnableViewState property of the containing page to false (only when the page is viewed anonymously). The net effect will be that ... you will still have the "ViewState" hidden field in your page, but it will contain only thirty or so bytes of data.
  • RemoveForm - when set this property removes the "form" element from your page. Actually it does something much more radical - it removes also all system hidden fields including the ViewState field and all inline JavaScript blocks that were included using the methods of Page.ClientScript - e.g. RegisterClientScriptBlock, RegisterStartupScript, etc. You will get rid of a ton of HTML and JavaScript in your page, which you wouldn't need if you don't have controls and logic that need to do POST submits of the page. If your pages (at least the pages using the master page with the HideSPArtifacts control) contain only SharePoint field controls and content editor web parts this will be a perfect choice. Note however that you will need to carefully check all your pages - some controls (like Button, LinkButton, etc) directly crash if there is no "form" rendered on the page. Other controls may stop function properly because they will miss some JavaScript code that won't get rendered. Bottom line - use cautiously.
  • AddBodyOnLoadDummy - the default value of this property is true. It has a visible effect only when the "RemoveForm" property is set to true. It adds a small JavaScript block with several empty JavaScript functions. One of these functions is called "_spBodyOnLoadWrapper". This function appears in the "onload" attribute of the "body" element of the default SharePoint master page. Since the "RemoveForm" property removes all inline JavaScript associated with the page's "form" element, the real definition of this JavaScript function won't be available on the page, and you will see a JavaScript error in your browser when the page loads. This is the reason why this property causes the adding of this small JavaScript block with empty definitions of this and two other system JS functions.

And now, let me briefly explain how the trick with hiding a control without hiding its contents is possible. Actually the idea is to hide the control itself (or at least parts of it) but display its child controls. This technique is used in the implementation of the "RemoveForm", "RemoveHeadCss" and "RemoveZoneHeaders" properties. The following steps are executed:

  • in its OnInit override the HideSPArtifacts hooks onto the parent page's InitComplete event
  • in the InitComplete event handler an empty control (class Control) is created in the "Controls" collection of the parent control of the control which we want to hide. The new control is inserted in the "Controls" collection of the parent control right after the target control.
  • The SetRenderMethodDelegate method of the new empty control is called - this method receives a single delegate parameter, which you use to provide a method to be called right after the control's "Render" method exits. The idea is to use the empty control as a place-holder to inject some HTML right after the control that we want to hide.
  • In the "Render" method override of the HideSPArtifacts control the "Visible" property of the control that we want to hide is set to false. Since we have placed the HideSPArtifacts right after the beginning of the master page its Render method is guaranteed to be called first in the child controls' chain. This way the control whose Visible property is set to false will not get rendered.
  • in the render method passed as the render delegate parameter of the SetRenderMethodDelegate method, the Controls collection of the target control is iterated and all child controls are rendered using the Control.RenderControl method. This way we have the target control itself not rendered but all its children get actually rendered within the empty control that was injected right after it. This is how the goal of hiding the control itself but not its child controls is achieved.

You can check the source code of the HideSPArtifacts control for the details of the actual implementation.

Saturday, November 26, 2011

Dynamic class system in C# 4.0

The "dynamic" type (rather pseudo-type or keyword) is a new feature in the latest version of C# - 4.0. Its purpose is to enable an easier interoperability with dynamic type languages and COM components and is part of the new DLR system introduced in the .NET Framework 4.0. In addition to the new "dynamic" type there are two interesting new built-in .NET classes that allow you to do some sort of dynamic type programming with C# - the ExpandoObject class and the DynamicObject class. Using the "dynamic" type/keyword and the ExpandoObject you can do things like this:
dynamic sampleObject = new ExpandoObject();
sampleObject.number = 10;
sampleObject.Increment = (Action)(() => { sampleObject.number++; });
sampleObject.Increment();

You can add dynamically member variables to the "dynamic" instance and also methods by assigning delegates or anonymous methods to instance members (you can also assign and use events in the same manner). These tricks are possible and the C# compiler doesn't raise compilation errors exactly because of the "dynamic" keyword. It ensures that  these assignments, member variable usages and method invocations are not checked during compile time but all that happens at run time. The dynamic dispatching semantics are implemented by the ExpandoObject class. It implements a special .NET interface called IDynamicMetaObjectProvider which is used as a bridge by the DLR for the correct routing of all these dynamic invocations and references at run time. It is possible to create custom classes implementing this interface but it is actually easier to do that by inheriting the second class that I mentioned - the DynamicObject class. The DynamicObject class is an abstract class and if you inherit your class from it you will be able to easily implement your custom dynamic member dispatching logic by overriding one or more of its virtual methods.
I will not go into further detail about the "dynamic" type/keyword and the ExpandoObject as this is quite a broad subject and there are many in depth articles already available in MSDN. Instead I will go directly to the subject of this posting which is the simple dynamic class system for C# 4.0 that I implemented.
First, let me briefly explain as to why I came with the idea of implementing a dynamic class system in C#. The main reason was to do it just for fun and to prove (at least to myself) the expression power of C#. As a side note this means that this implementation is not intended for any serious usage, I coded the whole thing in just a couple of hours, so it lacks any performance optimizations whatsoever, it doesn't support multi threading and is not thoroughly tested. So, basically the answer to the question, why would there be any need to add dynamic classes support to a powerful static type language like C# is the plainly sounding - just to demonstrate that it is possible.
And the immediate reason to do this was that when I was reading the MSDN documentation about the ExpandoObject class I started wondering why they made it only possible to add dynamically members to a single instance/object only instead of providing support for dynamic classes with which to enable the creation of many instances having a predefined set of members simultaneously. Of course, this can be somehow emulated with the ExpandoObject itself - one only needs to create a "factory" method that creates and returns instances of the ExpandoObject "initialized" with the desired set of members but I wanted something more coherent and self-contained. And also some real class support with at least some of the standard class system features like inheritance, polymorphism, etc.
You can download the implementation of the dynamic class system from here - this is a small console application that defines two public classes - "Class" and "ClassDefinition" (quite descriptive names) which contain the whole logic of the dynamic class system. The application contains also a big demonstration section with lots of verbose comments describing the details of the usage of the dynamic classes - I will have this entire section pasted at the end of this posting, so that you can get an idea of the syntactic peculiarities of these "dynamic classes". But before doing that let me quickly list the main features of this custom dynamic class system:
  • uses only C# 4.0 syntax without any external or extra language declarations or configurations
  • supports simple class inheritance - a class can inherit only one base class
  • classes are defined with imperative statements (as opposed to the declarative syntax for normal/static C# classes)
  • supports simple polymorphism - all methods are treated as virtual
  • supports class constructors and constructor overloading
  • supports method overloading - you can have two or more member methods with the same name but with different set of parameters
  • supports the "self" keyword for referencing member variables/methods from member methods (analogue to the C#'s native "this" keyword)
  • supports the "super" keyword for referencing methods defined in the parent class (if available)
  • objects (class instances) are "open" - you can initialize and use additional member variables and methods that are not defined in the class (similarly to the objects in JavaScript and the ExpandoObject itself)
  • class definitions are also "open" - you can extend the definition of any class at any later moment adding new member variables and methods (slightly influenced by Ruby). The new methods are immediately available in all class instances even those created before the extension of the class definition.
  • relaxed rules for uninitialized member variables and methods. You can reference not initialized member variables and this will not raise an error - but the value returned will be null. You can call not initialized (not existing) methods and this will not raise an error either - the invocation will do nothing and will return null.
  • doesn't support events
  • doesn't support access modifiers - all members are treated as public and accessible from any context
And here is the demonstration section describing the "syntax" of the dynamic class system:


// 'Class' is a static class with one static method - you can define dynamic classes with it: Class.DefineClass
// you specify the class name, the base class (null for none) and an Action delegate (lambda expression) that defines the class' members
Class.DefineClass(new ClassDefinition()
{
    ClassName = "BaseClass", 
    Variables = (def) =>     {
         // two member variables
         def.Register1 = 1;
         def.Register2 = 2;
     },
     Methods = (def, self) =>     {
         // the Init method is used as a constructor
         def.Init = new Action<intint>((a, b) => { self.Register1 = a; self.Register2 = b; });
         // define an Add method
         def.Add = new Func<intintint>((a, b) => a + b);
         // define an overload of the Add method that sums the member properties Register1 and Register2 - note the usage of the "self" lambda parameter
         def.Add = new Func<int>(() => self.Register1 + self.Register2);
         def.ProxyAdd = new Func<int>(() => { Console.Write("BaseClass.ProxyAdd calling Add(): "); return self.Add(); });
     } 
});
        
// now have a look at the static property "New" of the Class class, which is of type ... dynamic
// and we can call methods on this "New" dynamic object which will return instances of our custom dynamic classes with the same name // let's create an instance of the dynamic class Test calling its constructor that accepts a string paramter
// but ... we haven't defined a class named "Test" let alone a constructor of the "Test" class
// still the local variable c1 gets initialized with a "dynamic" instance which behaves pretty much like the standard ExpandoObject class
dynamic c1 = Class.New.Test("test");
// it doesn't have any properties but we can add one
c1.var1 = "test";
// this will print "test"
Console.WriteLine(c1.var1);
// what about the "var2" property - it hasn't been initialized so c1.var2 is simply null
Console.WriteLine(c1.var2 ?? "this is null");
// we don't have any methods in c1, but we can call whatever methods we want - they will all return null (and will do nothing of course)
Console.WriteLine(c1.test(1, 2) ?? "this is null");
// let's now create an instance of the "BaseClass" class, which we have already declared. This would call the parameterless Init method of the class (if it existed)
dynamic c2 = Class.New.BaseClass();
// call its Add(int, int) method with two integer parameters - result is 2 + 2 = 4
Console.WriteLine(c2.Add(2, 2));
// call the Add() overload with no parameters - this sums the object's properties Register1 and Register2 which have their initial values: 1 + 2 = 3
Console.WriteLine(c2.Add());
// let's create another instance of the BaseClass class. This will call its Init(int, int) method that takes two integer parameters.
dynamic c3 = Class.New.BaseClass(7, 8);
// calling the parameterless Add() overload will sum the Register1 and Register2 properties which were initialized to 7 and 8 respectively: 7 + 8 = 15
Console.WriteLine(c3.Add());
// the same: 7 + 8 = 15
Console.WriteLine(c3.ProxyAdd());
// now let's define a new class that inherits the BaseClass class
Class.DefineClass(new
ClassDefinition() {
     ClassName = "DerivedClass",
     BaseClassName = "BaseClass",
     Variables = (def) =>     {
         // declare a new property
         def.Register3 = 3;
     },
     Methods = (def, self) =>     {
         // declare a constructor that accepts three integers
         // note the usage of the self.super expression - we can call methods from the base class this way (not necessary in this case since the base method accepts two parameters and the new method - three, so the new method doesn't hide the overload in the base class and it can be called simply with self.Init(int, int))
         // note that the constructor of the base class is called explicitly
         def.Init = new Action<intintint>((a, b, c) => { self.super.Init(a, b); self.Register3 = c; });
         // redefining the parameterless Add() method, this one will hide the parameterless Add() method in BaseClass - to call the base class implementation we need to use self.super.Add()
         // we call both Add overloads of BaseClass - first to sum Register1 and Register2 and then to sum the result with Register3
         def.Add = new Func<int>(() => self.super.Add(self.super.Add(), self.Register3));
     }
});
// here is an instance of the DerivedClass class (no constructor will be called since we don't have a parameterless Init() method)
dynamic c4 = Class.New.DerivedClass();
// this will invoke the DerivedClass.Add() implementation which sums Register1, Register2 and Register3: 1 + 2 + 3 = 6
Console.WriteLine(c4.Add());
// this will invoke the BaseClass.Add method(int, int) with two integer parameters (it hasn't been redefined in DerivedClass): 4 + 4 = 8
Console.WriteLine(c4.Add(4, 4));
// create an instance of DerivedClass ... using BaseClass.Init (int, int), which hasn't been redefined in DerivedClass
dynamic c5 = Class.New.DerivedClass(10, 11);
// calling DerivedClass.Add() will return: 10 + 11 + 3 = 24
Console.WriteLine(c5.Add());
// create another instance of DerivedClass - this will call Init(int, int, int) which was declared in DerivedClass
dynamic c6 = Class.New.DerivedClass(100, 200, 300);
// calling DerivedClass.Add() will return: 100 + 200 + 300 = 600
Console.WriteLine(c6.Add());
// calling BaseClass.ProxyAdd which calls Add() which is the DerivedClass.Add() version since c6 is DerivedClass
Console.WriteLine(c6.ProxyAdd());
// Declare the DerivedClass class again? No, this will only extend the definition of DerivedClass - additional members can be added. No need to specify the base class again.
Class.DefineClass(new ClassDefinition {
     ClassName = "DerivedClass",
     Variables = (def) =>      {
         // add a new member property
         def.Register4 = 4;
     },
     Methods = (def, self) =>     {
         // add a new constructor accepting 4 integer parameters - this one calls the constructor (with three integer parameters) from the first DerivedClass declaration
         def.Init = new Action<intintintint>((a, b, c, d) => { self.Init(a, b, c); self.Register4 = d; });
         // redefine again the parameterless Add method - this one will sum all properties: Register1, Register2, Register3 and Register4
         // note the tree calls to self.super.Add - two to self.super.Add(int, int) and one to self.super.Add()
         def.Add = new Func<int>(() => self.super.Add(self.super.Add(), self.super.Add(self.Register3, (self.Register4 ?? 0))));
     }
});
// the c6 instance was created before the extending of the DerivedClass definition - but it will use the new version of DerivedClass.Add()
// it doesn't have the Register4 property initialized - it's value will be null (the Add() will add it as 0)
// 100 + 200 + 300 + 0 = 600
Console.WriteLine(c6.Add());
// set Register4 explicitly
c6.Register4 = 400;
// 100 + 200 + 300 + 400 = 1000
Console.WriteLine(c6.Add());
// again 100 + 200 + 300 + 400 = 1000
Console.WriteLine(c6.ProxyAdd());
// c7 is created calling the constructor DerivedClass.Init(int, int, int) declared in the first definition of DerivedClass
dynamic c7 = Class.New.DerivedClass(1000, 2000, 3000);
// its Add method will now (as defined in the extension of DerivedClass) sum Register1, Register2, Register3 and Register4: 1000 + 2000 + 3000 + 4 = 6004
Console.WriteLine(c7.Add());
// c8 is created with the constructor DerivedClass.Init(int, int, int, int) defined in the second definition (extension) of DerivedClass
dynamic c8 = Class.New.DerivedClass(1000, 2000, 3000, 4000);
// its Add method will now sum Register1, Register2, Register3 and Register4: 1000 + 2000 + 3000 + 4000 = 10000
Console.WriteLine(c8.Add());
// we can call the parameterless constructor without the parenthesis
dynamic c9 = Class.New.DerivedClass;
// it's possible to call the Init method afterwards
c9.Init(10000, 20000);
// let's check that it's a different instance from c9 - see below
dynamic c10 = Class.New.DerivedClass;
Console.WriteLine();
// a built-in diagnostics method - dumping all member properties of the dynamic objects
Console.WriteLine(c1.dump());
Console.WriteLine(c2.dump());
Console.WriteLine(c3.dump());
Console.WriteLine(c4.dump());
Console.WriteLine(c5.dump());
Console.WriteLine(c6.dump());
Console.WriteLine(c7.dump());
Console.WriteLine(c8.dump());
Console.WriteLine(c9.dump());
Console.WriteLine(c10.dump());

Sunday, October 30, 2011

Deploy user controls in the SharePoint content database

Normally user controls (*.ascx files) in SharePoint are deployed with farm solutions and the preferred system  (hive) folder for that is the CONTROLTEMPLATES one. User controls can be used for various purposes – for form templates in SharePoint lists, with the new visual web part in SharePoint 2010 or simply to provide reusable visual bits that can be placed in more than one master page files of page layouts. User controls can be quite handy since they provide a nice separation of the presentation logic which is not directly available in the regular web controls – you can use the WebForms markup to produce your HTML output instead of having to deal with that in the code itself.

And to the subject of this posting – is it possible to have user controls (*.ascx) files directly in the content database of your SharePoint web application (site) instead of in the SharePoint hive folder (12/TEMPLATE/CONTROLTEMPLATES or 14/TEMPLATE/CONTROLTEMPLATES). The answer to this question is yes and I am going to demonstrate that shortly. But before that I would want to briefly discuss the motives and reasons that could justify the usage of user controls in the content database and also some possible advantages and disadvantages of this approach. This topic is actually a bit broader than suggested by the posting’s title and it is about the possibility of having a SharePoint custom application that doesn’t use custom assemblies and files deployed to the SharePoint hive folder. Sounds a bit like the new sandbox solutions available in SharePoint 2010 – this is to some extent so, though the code that can be executed is the normal farm version of the SharePoint object model. Unlike the sandbox solutions, there is also a big security implication that I will discuss shortly. The main motivation here is to have a way to place your code directly in your master pages or page layouts by using say the SharePoint designer. So, opposed to the usual way of deploying SharePoint farm solutions with “wsp” files, this is sorts of “SharePoint designer” development and deployment (the boundary between development and deployment with the SharePoint designer is almost non-existing). The other advantage here is that you can push all you code and code updates using the simple content deployment paths and jobs built-in functionality. The bottom line here is that if you use extensively the SharePoint designer and have a SharePoint environment with a publishing and production server using content deployment paths for content synchronization you can consider using this approach.

As for the question of how to use code directly in your master page files and page layouts (maybe you’ve done that many times already with the SharePoint designer) – the answer is simple – inline code blocks:

<script runat="server">

Two big notes here – the first one is the security issue that I mentioned above. Since it is very easy to insert a code block in a page using the SharePoint designer, there is an internal protection in SharePoint – this is the so called “safe mode” for parsing and processing of un-ghosted pages (pages that are in the content database). By default if you have a code block in a page that is un-ghosted (or was created directly in the content database) you will receive an error if you try to open the page. This can be overridden by a setting in the web.config file:

<PageParserPaths><PageParserPath VirtualPath="/_catalogs/masterpage/*" CompilationMode="Always" AllowServerSideScript="True" IncludeSubFolders="true" />
</PageParserPaths>

but keep in mind that this can be a serious security hole. Note the value of the “VirtualPath” attribute containing the location of the Master Page gallery with a wild card meaning that all your master page files and page layouts will be allowed to have code blocks. If you have a site collection under this server relative URL – “/sites/test-site” you will have to specify the full path to its Master Page gallery: “/sites/test-site/_catalogs/masterpage/*” (unless you don’t want to put something as unsecure as “/*”). For a detailed treatment of the SharePoint “safe mode” page processing, the configuration of the “PageParsePath” elements (and also of the “SafeControl” elements) check this MSDN article.

The second note is that the usage of code blocks as opposed to having the code in a code-behind assembly is not considered the very best and recommendable code practice. Apart from that the SharePoint designer is far from Visual Studio in terms of providing good IDE support for code development.

The placing of code blocks inside master pages and page layouts is maybe nothing new for most of you but the main idea of this posting is to demonstrate the using of user controls inside master pages and page layouts. And it will be the user control that will contain the inline code block in this case. Remember that the ascx file of the user control will reside in the content database (it can be uploaded to the Master Page gallery for instance), so it will be subject to the SharePoint “safe mode” processing mode too. And you will need to add some extra configuration bits to the web.config file so that your pages are allowed to use the user controls. You have two ways to enable this in the web.config – the first one is to add a “SafeControl” element like this one:

<SafeControl Src="/_catalogs/masterpage/*" IncludeSubFolders="True" Safe="True" AllowRemoteDesigner="True" SafeAgainstScript="True" />

Note that the value of the “Src” attribute should contain the full server relative URL of the target library in your site collection (e.g. for the “/sites/test-site site collection it will be /sites/test-site/_catalogs/masterpage/*”). The other way (much more unsecure) is to add an extra attribute to the “PageParsePath” element:

AllowUnsafeControls="True"

In this case you won’t need an extra “SafeControl” element.

Note also that although you can deploy your code files (pages and user controls) directly to the content database unlike the SharePoint 2010 sandbox solutions you will need to modify the web.config file of the containing web application, which has its own serious security implication, that I already pointed out.

And let me now demonstrate a sample user control that can be deployed (uploaded) to the Master Page gallery and used by page layouts (wpzone.ascx):

<%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
 
<%@ Control Language="C#"  %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.SharePoint.WebPartPages" %>
 
<script runat="server">
    protected string _wpZoneID;          
    public string WPZoneID { get { return _wpZoneID; } set { _wpZoneID= value; } }
    protected override void OnLoad (EventArgs args)
    {
         base.OnLoad(args);
    }
 
    protected override void Render (HtmlTextWriter writer)
    {
         if (string.IsNullOrEmpty(_wpZoneID)) return;
         Control c = this.NamingContainer.FindControl (_wpZoneID);
         WebPartZone _zone = c as WebPartZone;
         if (_zone == null) return;
         SPWebPartManager mngr = SPWebPartManager.GetCurrentWebPartManager(this.Page) as SPWebPartManager;
         if (mngr == null) return;
         if (!mngr.GetDisplayMode().AllowPageDesign)
         {
             // if we are in display mode - hide the zone control itself
             _zone.Visible = false;
             // and render the web parts directly
             foreach (WebPart part in _zone.WebParts)
             {
                 part.RenderControl(writer);
             }
         }
     }
script> 


And here is how you can use this control in a page layout (the technique is identical for master pages and regular web part pages) – first you need the control “Register” directive at the top of the page:


<%@ Register TagPrefix="Test" TagName="WPZone" Src="~SiteCollection/_catalogs/masterpage/wpzone.ascx" %>


Note the value of the “Src” attribute – you can use the handy “~SiteCollection” URL token here instead of having to hard-code the server relative URL of the containing site collection. And then the declaration of the user control’s tag inside the markup of the page:


<Test:WPZone runat="server" WPZoneID="TopZone"/>


This simple user control modifies the default rendering of the WebPartZone control whose ID is specified in its “WPZoneiD” property. When the page is in display mode the control hides the web part zone and renders only the web parts that belong to this web part zone. This effectively hides the markup that is produces by the web part zone (several nested HTML table elements) and also the chrome (or frame) headers of the web parts. You should place the control just before the declaration of the web part zone control whose rendering you want to modify.


It is also possible to use user controls just as regular classes with helper methods that can be reused in various places – in this case the user control will not have visual behavior. For example you can create a user control like this (mylib.ascx):


<%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
 
<%@ Control Language="C#" ClassName="MyLib" %>
<script runat="server">
 public static string SayHello(string who)
 {
  return "Hello " + who;
 }
 
 public string SayHi (string who)
 {
  return "Hi " + who;
 }
script> 


Note the “ClassName” attribute in the “Control” directive – this specifies the name of the class that will be generated from the ascx file. You will be able to use this generated class by this name in the code blocks of the pages that use the control (or in other user controls). To use the user control in this way you will only have to put the control “Register” directive at the top of the page. And then in a code block you can have something like:


<script runat="server">
protected override void OnLoad(EventArgs args)
{
 base.OnLoad(args);
 this.txtBox.Text = MyLib.SayHello("John");
}
script>

Sunday, September 11, 2011

SharePoint 2010–activate features with feature properties

There are two ways to achieve this in SharePoint – specify the feature properties in the ONET.XML file of your site definition or web template (the new SharePoint 2010 feature) or put the properties directly in the FEATURE.XML definition file of the feature. The latter is almost useless since it almost defeats the purpose of having the type of parameterization that you can achieve with feature properties – which is a very simple and powerful concept – use one and the same feature which can act differently in different contexts. As for the ONET.XML usage of feature properties – I think that this is probably the only thing that justifies the usage of site templates at least for me. In this sense site definitions are not merely a set or a grouping of features that should be activated to a site, provisioning some SharePoint artifacts, but rather a grouping of specifically configured features with this configurability being possible with the usage of feature properties. So despite the numerous issues that one may encounter with site definitions in SharePoint especially when it comes to updating the definition in existing sites, site definitions still have this important and pretty useful bit to offer.

The next question is can this same thing be achieved programmatically. In the SharePoint object model we have the SPFeatureCollection.Add method which allows you to activate features to a site collection or a site. It actually comes with three overloads but none of them allows you to provide specific feature properties that should be used for the feature activation. Of course if you are the “hacker” type of developer and use frequently the .NET Reflector utility you will probably already know that there is an internal method in the aforementioned SPFeatureCollection class that actually accepts a parameter in which you can specify the feature properties for activation. If you Google this you will find that there are actually several blog articles describing this technique. There is one big problem with reflection though – apart from the fact that it is not one of the best practices to use, there is always the risk to get things broken with the next service pack or version of SharePoint. In fact the notation of this exact method changed in SharePoint 2010 as compared to SharePoint 2007.

But is reflection the only option that we have here? It turns out that there is one other way to get this working and you can get an idea of how this is possible again by using the .NET Reflector tool (quite popular for SharePoint development actually). If you use the “analyze” command for the SPFeatureCollection.AddInternal method (the internal method, that I mentioned above) in the .NET Reflector you will see that it is used directly by an internal class called FeatureSerializer from the Microsoft.SharePoint.Deployment namespace. The latter is a namespace in the core Microsoft.SharePoint.dll which contains the SharePoint Deployment API and this is an important clue. If you have done export and import operations for SharePoint site collections or sites you’ve probably noticed that the imported site collections and sites are pretty much exact replicas of the source site collections and sites and that they contain all features with exactly the same feature properties activated as their source counterparts. It is obvious that the deployment API handles not only SharePoint artifacts like SharePoint lists and list items but for sites and site collections the associated features as well. This is where the SPFeatureCollection.AddInternal internal method comes into play invoked by the deployment API.

So, we know that if you have an export package of a site or a site collection (created with the STSADM utility or PowerShell) it will activate the very same set of features in a target site or site collection (or will create them if they don’t exist) but it will also import a ton of other things like all SharePoint lists and list items in them. We face two questions here – one is – is it possible to create an export package that can activate only features to a target site or site collection and the second is – is it legal and allowed by SharePoint standards to create Deployment API packages manually, i.e. without using the Deployment API itself. Starting with the second – this is perfectly OK, the schema of the XML files in a SharePoint deployment package is described officially in MSDN - http://msdn.microsoft.com/en-us/library/bb249989.aspx – the idea is that it can be used by third party providers who want to import content in SharePoint. If you check the article in MSDN you will see that there are eight deployment schema files that a deployment package should contain (although not all of them are actually required). For all deployment schema files there are XSD files which you can find in the 14/TEMPLATE/XML folder (all starting with “Deployment”) and which you can use to validate the XML of the deployment schema files that you create.

And to answer the first question – this can be achieved by creating three small deployment schema files that look like this:

ExportSettings.xml:

<ExportSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" SiteUrl="http://mysite/sites/2" FileLocation="" BaseFileName="" IncludeSecurity="None" IncludeVersions="LastMajor" ExportPublicSchema="true" ExportFrontEndFileStreams="true" ExportMethod="ExportAll" ExcludeDependencies="false" xmlns="urn:deployment-exportsettings-schema">
  <ExportObjects>
    <DeploymentObject Id="ea0d5873-cf3a-4015-b015-db74f39db27a" Type="Web" ParentId="00000000-0000-0000-0000-000000000000" Url="" ExcludeChildren="false" IncludeDescendants="None" />
  </ExportObjects>
</ExportSettings>

SystemData.xml:

<SystemData xmlns="urn:deployment-systemdata-schema">
  <SchemaVersion Version="14.0.0.0" Build="14.0.4762.1000" DatabaseVersion="3683" SiteVersion="0" ObjectsProcessed="1" />
  <ManifestFiles>
    <ManifestFile Name="Manifest.xml" />
  </ManifestFiles>
  <SystemObjects></SystemObjects>
  <RootWebOnlyLists />
</SystemData>

Manifest.xml:

<SPObjects xmlns="urn:deployment-manifest-schema">
  <SPObject Id="04a18cb4-39d8-4371-8a21-d11f2b54a333" ObjectType="SPFeature" ParentId="" ParentWebId="" xmlns="urn:deployment-manifest-schema">
    <Feature Id="04a18cb4-39d8-4371-8a21-d11f2b54a333" FeatureDefinitionName="WTSample_SiteScopedFeature" Version="0.0.0.0" IsUserSolutionFeature="false" Properties="&lt;Properties&gt;&#xD;&#xA;  &lt;Property Key=&quot;a&quot; Value=&quot;b&quot; /&gt;&#xD;&#xA;&lt;/Properties&gt;" />
  </SPObject>
  <SPObject Id="4edea5b8-7da5-46cc-b1c6-7a8ce4bcb899" ObjectType="SPFeature" ParentId="" ParentWebId="ea0d5873-cf3a-4015-b015-db74f39db27a" xmlns="urn:deployment-manifest-schema">
    <Feature Id="4edea5b8-7da5-46cc-b1c6-7a8ce4bcb899" FeatureDefinitionName="WTSample_WTMarkFeature" Version="1.0.0.0" IsUserSolutionFeature="false" WebId="ea0d5873-cf3a-4015-b015-db74f39db27a" Properties="&lt;Properties&gt;&#xD;&#xA;  &lt;Property Key=&quot;c&quot; Value=&quot;d&quot; /&gt;&#xD;&#xA;&lt;/Properties&gt;" />
  </SPObject>
</SPObjects>

You see that the important bits are in the “manifest.xml” schema file – you can see two “SPObject” elements in it with one “Feature” element directly beneath each – these correspond to two features – one “Site” and one “Web” scoped. Have a look at the “Properties” attribute of the “Feature” element – you can see there a familiarly looking “Properties” XML fragment, exactly the same as you have in a standard feature.xml file.

After you have these three schema deployment files you can simply place them in a disc folder and use the deployment API with the SPImport.Run method to import the package as any normal deployment package (note that you will have an uncompressed package as opposed to the compressed packages that STSADM and PowerShell normally output).

An important note here – you can activate only features that are not already activated on the target site or site collection in this manner. If the features are already activated they won’t get reactivated as you can do with the STSADM utility or PowerShell using the “-force” parameter.

If you want to use this approach, instead of formatting the deployment schema files yourselves you can use a handy helper method that I created which nicely wraps the whole logic of the deployment package creation and the running of the SPImort.Run method. You can download the sample solution containing this method from here.

You can also see the source code of the helper method here (it uses some additional private helpers and helper classes whose definitions you can see in the full source code from the above link):

        // provide the target SPWeb and one or several FeatureInfo instances each containing a SPFeatureDefinition and Dictionary<string, string> (for the feature properties) pair

        // you can provide features with Site and Web scopes, Site scoped features are valid only for a root SPWeb

        public void EnableFeature(SPWeb web, params FeatureInfo[] infos)

        {

            // check for valid feature scopes

            bool valid = infos.All(info => info.FeatureDefinition.Scope == SPFeatureScope.Web || (info.FeatureDefinition.Scope == SPFeatureScope.Site && web.IsRootWeb));

            if (!valid) throw new ArgumentException("Invalid feature scope.");

 

            // load the template SystemData.xml, ExportSettings.xml, Manifest.xml files which are stored as embedded resources

            XDocument systemDataDoc = XDocument.Parse(this.GetManifestResource("WTSample.Core.Xml.SystemData.xml"));

            XDocument exportSettingsDoc = XDocument.Parse(this.GetManifestResource("WTSample.Core.Xml.ExportSettings.xml"));

            XDocument manifestDoc = XDocument.Parse(this.GetManifestResource("WTSample.Core.Xml.Manifest.xml"));

 

            XNamespace ns = systemDataDoc.Root.Name.Namespace;

            XElement schemaVersionElement = systemDataDoc.Root.Element(ns + "SchemaVersion");

            // set the Build and DatabaseVersion attributes in the SystemData.xml

            schemaVersionElement.SetAttributeValue("Build", SPFarm.Local.BuildVersion.ToString());

            schemaVersionElement.SetAttributeValue("DatabaseVersion", web.Site.ContentDatabase.Version.ToString(CultureInfo.InvariantCulture));

 

            ns = exportSettingsDoc.Root.Name.Namespace;

            IXmlNamespaceResolver resolver = exportSettingsDoc.Root.CreateNavigator();

            // set the SiteUrl and Id attributes in the ExportSettings.xml

            exportSettingsDoc.Root.SetAttributeValue("SiteUrl", web.Url);

            XElement deploymentObject = exportSettingsDoc.Root.Descendants(ns + "DeploymentObject").FirstOrDefault();

            if (deploymentObject != null) deploymentObject.SetAttributeValue("Id", web.ID);

 

            ns = manifestDoc.Root.Name.Namespace;

            // extract the existing SPObject element from the Manifest.xml - we will use it as a template for adding the feature SPObject nodes

            XElement featureElement = manifestDoc.Root.Elements(ns + "SPObject").First();

            string featureElementStr = featureElement.ToString();

            featureElement.Remove();

 

            foreach (FeatureInfo info in infos)

            {

                // create a new feature SPObject element for every feature definition

                featureElement = XElement.Parse(featureElementStr);

                manifestDoc.Root.Add(featureElement);

 

                // set its Id and ParentWebId (the latter only for Web scoped features)

                featureElement.SetAttributeValue("Id", info.FeatureDefinition.Id.ToString());

                if (info.FeatureDefinition.Scope == SPFeatureScope.Web) featureElement.SetAttributeValue("ParentWebId", web.ID.ToString());

 

                // get the feature's name and the feature's parent solution type

                bool isUserSolution = false;

                string rootDirectory = info.FeatureDefinition.RootDirectory;

                int startIndex = rootDirectory.LastIndexOf(@"\") + 1;

                if (startIndex >= 0) { rootDirectory = rootDirectory.Substring(startIndex); }

                if (rootDirectory.Length == 0)

                {

                    isUserSolution = true;

                    rootDirectory = info.FeatureDefinition.GetTitle(CultureInfo.InvariantCulture) ?? "";

                }

 

                // create the child Feature element

                XElement contentElement = featureElement.Element(ns + "Feature");

                // set its Id, FeatureDefinitionName, Version, IsUserSolutionFeature and WebId attributes

                contentElement.SetAttributeValue("Id", info.FeatureDefinition.Id.ToString());

                contentElement.SetAttributeValue("FeatureDefinitionName", rootDirectory);

                contentElement.SetAttributeValue("Version", info.FeatureDefinition.Version.ToString(4));

                contentElement.SetAttributeValue("IsUserSolutionFeature", isUserSolution.ToString().ToLower());

                if (info.FeatureDefinition.Scope == SPFeatureScope.Web) contentElement.SetAttributeValue("WebId", web.ID.ToString());

                if (info.Properties != null && info.Properties.Count > 0)

                {

                    // create Properties XML fragment

                    XElement propertiesElement = new XElement("Properties", info.Properties.Select(kv => new XElement("Property", new XAttribute("Key", kv.Key), new XAttribute("Value", kv.Value))));

                    // and assign its value to the Properties attribute

                    contentElement.SetAttributeValue("Properties", propertiesElement.ToString());

                }

 

                if (string.IsNullOrEmpty(contentElement.Attribute("WebId").Value)) contentElement.Attribute("WebId").Remove();

                if (string.IsNullOrEmpty(contentElement.Attribute("Properties").Value)) contentElement.Attribute("Properties").Remove();

            }

            // create a temporary folder to store the export schema files

            string tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

            Directory.CreateDirectory(tempFolder);

            try

            {

                // create the export schema files

                systemDataDoc.Save(Path.Combine(tempFolder, "SystemData.xml"));

                exportSettingsDoc.Save(Path.Combine(tempFolder, "ExportSettings.xml"));

                manifestDoc.Save(Path.Combine(tempFolder, "Manifest.xml"));

                // set up the SPImportSettings for the created export batch

                SPImportSettings importSettings = new SPImportSettings()

                {

                    FileCompression = false,

                    FileLocation = tempFolder,

                    SiteUrl = web.Site.Url,

                    WebUrl = web.Url

                };

                // run the SPImport.Run methods to import the batch

                SPImport import = new SPImport(importSettings);

                import.Run();

            }

            finally

            {

                Thread.Sleep(0);

                // clean up - delete the temporary folder

                try { Directory.Delete(tempFolder, true); }

                catch { }

            }

        }

The code contains pretty verbose comments, so that you can easily see how the three deployment schema XML files are formatted and how the input bits from the target SPWeb instance and the feature or features to be activated are fitted in XML elements or attributes inside them. The method accepts a SPWeb input parameter but you can use it for activating “Site” scoped features as well, as long as the SPWeb instance represents the root site of the target site collection.

One last thing to mention – this approach (at least its implementation) may seem a bit complicated and overly verbose as opposed to the one-liner solution when you use reflection. Another thing is that the deployment schema files can hardly be seen as a sort of a contract and we cannot expect that their format won’t get changed in the future in some next release of version of SharePoint. Still, one thing that I find important is that the very same approach of constructing manually deployment packages can as well be used to provision other types of SharePoint artifacts that we thought so far are possible only with XML in feature element files which I think can reveal the great potential of the SharePoint deployment API in this field.