Create list views across webs using PowerShell or code

Sometimes you’ll want to have one basic list, which is accessible on all your subsites. One way to do this is using the content query web part. This however, blocks the functionalities a standard list view offers. So instead of the content query web part, we’ll use the basic XsltListViewWebPart. An important note: the standard ListViewWebPart class cannot work across your different webs inside your site collection. You have to use the Xslt one.

First, get an instance of the list you want to reference in your subsite:

$webWithList = Get-SPWeb http://weburl
$list = $webWithList.Lists["ListName"]

Then, get the web part manager of the page of your subsite where you want to insert the list view:

$targetWeb = Get-SPWeb http://weburl/subweb
$wpm = $targetWeb.GetLimitedWebPartManager("default.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared);

Now that we’ve got both required objects, it’s time to create the XsltListViewWebPart:

$ListViewWebPart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$ListViewWebPart.ListId = $list.id
$ListViewWebpart.Title = $list.title
$ListViewWebPart.WebId = $list.parentweb.id

It’s very important the WebId is set or otherwise you’ll get a nasty error like this:

“List does not exist. The page you selected contains a list that does not exist. It may have been deleted by another user.”

Last but not least, add the web part to your page:

$wpm.AddWebPart($ListViewWebPart, "Left", 0)

In one flow the script looks like this:

$webWithList = Get-SPWeb http://weburl
$list = $webWithList.Lists["ListName"]
$targetWeb = Get-SPWeb http://weburl/subweb
$wpm = $targetWeb.GetLimitedWebPartManager("default.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared);
$ListViewWebPart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$ListViewWebPart.ListId = $list.id
$ListViewWebpart.Title = $list.title
$ListViewWebPart.WebId = $list.parentweb.id
$wpm.AddWebPart($ListViewWebPart, "Left", 0)

If you would like for instance feature stapling, you can use the following code block to your web feature activation:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var web = properties.Feature.Parent as SPWeb;
    using (var rootWeb = web.Site.OpenWeb())
    {
        var list = rootWeb.Lists[Constants.ListName];
        string file = web.RootFolder.WelcomePage;
        var webPartManager = web.GetLimitedWebPartManager(file, PersonalizationScope.Shared);
        var webPart = new XsltListViewWebPart
        {
            ListId = list.ID,
            Title = list.Title,
            WebId = list.ParentWeb.ID
        };
        webPartManager.AddWebPart(webPart, "Left", 0);
    }
}

Creating a new SharePoint site programmatically

Creating sites through code adds the ability to package them in .wsp files. When developed applications have to be pushed to a production environment, recreating all created sites manually can be a cumbersome task. By using code, we’re able to automate this task.

I created a new Empty SharePoint project and added an event receiver. Next, uncomment both FeatureActivated and FeatureDeactivating code blocks. Open up your code file and add the following variables:

const int LOCALE_ID = 1033;
const string SITE_NAME = "Site";
const string SITE_TITLE = "Site Title";
const string SITE_DESCRIPTION = "This is your new site!";
const string SITE_TEMPLATE = "STS#1";

The variable names are self-explanatory. There’s a complete list available with all the IDs.

The following line will create the new SharePoint site:

SPContext.Current.Web.Webs.Add(SITE_NAME, SITE_TITLE, SITE_DESCRIPTION, LOCALE_ID, oWebTemplate, false, false);

To clean things up a bit and check for validation, I use the following FeatureActivated code block:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPWeb oWeb = SPContext.Current.Web;
    SPWebTemplate oWebTemplate = oWeb.GetAvailableWebTemplates(LOCALE_ID)[SITE_TEMPLATE];
    SPWebCollection cWebs = oWeb.Webs;

    SPWeb tWeb = cWebs[SITE_NAME];
    if (!tWeb.Exists)
    {
        SPWeb xWeb = cWebs.Add(SITE_NAME, SITE_TITLE, SITE_DESCRIPTION, LOCALE_ID, oWebTemplate, false, false);
        xWeb.Dispose();
    }
    tWeb.Dispose();
}

Next, code the FeatureDeactivating block:

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
    SPWeb oWeb = SPContext.Current.Web;

    if (oWeb.Webs[SITE_NAME].Exists)
    {
        try
        {
            oWeb.Webs.Delete(SITE_NAME);
        }
        catch (Exception)
        {

            throw;
        }
    }
}

That should do it! Also make sure your feature doesn’t get enabled by default. Double click on the feature file and switch “Activate On Default” to false:

activate feature by default

Add an event receiver to a specific list programmatically

Creating an event receiver allows you to catch events for specific list types. Such as custom lists, document libraries, announcements, … When we want to catch an event for a specific list, the standard procedure for creating an event receiver changes a bit.

There’s an article on MSDN which describes a method by editing the elements.xml file of an event receiver project. However, the same result can be achieved with code.

Basically, an event receiver template will create a feature file which holds a reference to the elements.xml file from the event receiver project. Inside this elements.xml file, the properties of the event receiver are defined, such as the type of list it will be bound to. We want to bypass the elements.xml file and manually attach the event receiver to a specific list’s event receivers. The binding itself will be written in a feature event receiver.

Start out with creating a new SharePoint 2010 project and select the Event Receiver template. Fill in any desired properties and click on OK:

new event receiver project

Next, fill in the site where the list is at you want to attach the event receiver. Also choose “farm solution” as the desired trust level.

link farm solution

Click next, choose the event receiver settings. I’ve chosen for “List Item Events” where “An item was added” should be handled. The event source isn’t important, since we’ll override the binding:

event receiver settings

When finish is clicked, Visual Studio will create the files based on the template and the chosen settings. A feature will be created with a reference to the EventReceiver1 project. Inside this project an elements.xml file and a code file will be created. The elements.xml file holds the settings of the event receiver. The code file holds the code that will be executed:

event receiver solution explorer

Whenever you want to add more events that have to be captured, click on the EventReceiver1 project and look at the properties window. Here you can enable or disable events that have to be captured. Note that when you disable an already enabled event, the code inside the code file will not be removed. This is a safety built-in for when you accidentally disable an event:

event receiver properties

The next step is removing the connection between the feature and the event receiver project. By doing this we avoid that the elements.xml file is used to bind the event receiver. Double click on the feature file, select the EventReceiver1 project and click on the “<” arrow to remove the project from the feature:

remove event receiver from feature

Save the modification, and right-click the feature file to add a feature event receiver:

add event receiver

If it’s not already open, double-click the feature event receiver code file to open it up. Next, uncomment both FeatureActivated and FeatureDeactivating code blocks.

First, declare two constants in your feature class to keep constancy:

        const string _listName = "test";
        const SPEventReceiverType _eventType = SPEventReceiverType.ItemAdded;

Next, write the following code in the FeatureActivated block:

        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPWeb oWeb = (SPWeb)properties.Feature.Parent;
            oWeb.Lists[_listName].EventReceivers.Add(_eventType, Assembly.GetExecutingAssembly().FullName, "EventReceiverProject1.EventReceiver1.EventReceiver1");
        }

You’ll notice that the Assembly namespace isn’t recognized so add the following line in your using statements:

using System.Reflection;

The last parameter of the Add() function can be found in the elements.xml file of the EventReceiver1 project. When you open this file you will see the 2 tags that are also referenced here in our code:

        <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
        <Class>EventReceiverProject1.EventReceiver1.EventReceiver1</Class>

$SharePoint.Project.AssemblyFullName$ is replaced by Assembly.GetExecutingAssembly().FullName in the code.

The following code will remove the event receiver that was added:

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPWeb oWeb = (SPWeb)properties.Feature.Parent;

            SPList oList = oWeb.Lists[_listName];
            for (int i = oList.EventReceivers.Count - 1; i >=0 ; i--)
            {
                if (oList.EventReceivers[i].Type.Equals(_eventType))
                {
                    try
                    {
                        oList.EventReceivers[i].Delete();
                    }
                    catch (Exception e)
                    {
                        // Write to logs
                    }
                }
            }
        }

Enabling Blob Cache programmatically

Whenever someone comes with the idea of editing the web.config file, I shiver. We’ve already had some issues where the Microsoft SharePoint Foundation Web Application had to be restarted. SharePoint then deletes all the websites from IIS and recreates them from scratch where, obviously, custom changes aren’t applied.

A solution for this problem is either by using custom web.config xml files in the hive, or programmatically by using SPWebConfigModification. The MSDN article explains a lot.

I only wanted to modify the web.config of one specific web application, so I used the SPWebConfigModification through a feature.

I started out with an empty SharePoint project and added a feature:

add feature

After your feature is added, change the scope to Web Application. You really don’t want your site collection administrators fiddling with your web.config files. Also add an event receiver so we can add code to our project.

add event receiver

When your event receiver is added, the cs file will pop-up in visual studio. If it doesn’t do this simply open the added cs file under your feature file.

The next step is enabling the FeatureActivated and FeatureDeactivating code blocks. Simply uncomment them.

What I wanted to achieve is enabling the BlobCache and put the size to 5GB. I used the following code block for FeatureActivated:

        const string owner = "BLOBCache";

        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPWebApplication webApplication = (SPWebApplication)properties.Feature.Parent;

            // Write the modification
            SPWebConfigModification webMod = new SPWebConfigModification();
            webMod.Path = "configuration/SharePoint/BlobCache";
            webMod.Name = "enabled";
            webMod.Value = "true";
            webMod.Sequence = 0;
            webMod.Owner = owner;
            // SPWebConfigModificationType.EnsureChildNode
            // SPWebConfigModificationType.EnsureAttribute
            // SPWebConfigModificationType.EnsureSection
            webMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute;

            // Write the modification
            SPWebConfigModification webMod2 = new SPWebConfigModification();
            webMod2.Path = "configuration/SharePoint/BlobCache";
            webMod2.Name = "maxSize";
            webMod2.Value = "5";
            webMod2.Sequence = 0;
            webMod2.Owner = owner;
            webMod2.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute;

            // Apply the modification
            webApplication.WebConfigModifications.Add(webMod);
            webApplication.WebConfigModifications.Add(webMod2);
            webApplication.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
            webApplication.Update();
        }

By using the following code I was able to modify the already existing BlobCache tag to “enabled” and put the size value from “10” to “5”!

It’s very important to keep track of the owner of the web modification. By using this owner we can track all changes made by a specific solution, and if necessary remove them. If we want to reverse our modifications we can use the following code block in FeatureDeactivating:

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPWebConfigModification configModFound = null;
            SPWebApplication webApplication = (SPWebApplication)properties.Feature.Parent;
            Collection<SPWebConfigModification> modsCollection = webApplication.WebConfigModifications;

            // Find the most recent modification of a specified owner
            int modsCount1 = modsCollection.Count;
            for (int i = modsCount1 - 1; i > -1; i--)
            {
                if (modsCollection[i].Owner == owner)
                {
                    configModFound = modsCollection[i];
                }
            }

            // Remove it and save the change to the configuration database
            modsCollection.Remove(configModFound);
            webApplication.Update();

            // Reapply all the configuration modifications
            webApplication.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
        }

Because the owner is a constant it’s easy to keep track the modifications made by the feature.

By making the feature specifically for one web application, we don’t want it to activate by default. Change the property “Activate On Default” to “False”. You can find the property by double clicking on the feature file, then it will show up in the properties window:

activate feature by default

When the solution is deployed to the farm, go to your central administration and click “Manage Web Applications”. Select the web application you want to enable the web.config modifications and click “Manage Features”.

manage web application features

When you click “Activate” next to your feature the web.config modifications should be made and you should see that your feature is active in the web feature dialog:

activate feature on web application

When the web.config file is checked, you’re able to see the modification has been applied!

blobcache enabled true

Now whenever a web front end server is added, or the web application service has to be restarted, the web.config modifications will be automatically applied.