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);
    }
}

Remove items from a huge SharePoint list

At a certain client of mine, there was a huge list consisting over 67 million items. This is well over the suggested 30 million item limit. This list was filled with random information and was adding lots of data to the dbo.AllUserData table so we decided to remove it. After trying several approaches, we couldn’t get past the huge DELETE statement SQL builds in order to remove the list. This statement was so large, it even crashed the SQL server. One approach that worked was using the GUI to delete items. As you can guess, this would take a huge amount of mandays to click away the list ūüėČ

Thankfuly SharePoint accepts batch updating items so I’ve written a script to delete those items in no time (subject to your processing power).

$web = get-spweb http://
$list = $web.lists | ? { $_.title -eq "<<ListTitle>>" }
$spQuery = New-Object Microsoft.SharePoint.SPQuery
$spQuery.ViewAttributes = "Scope='Recursive'";
$spQuery.RowLimit = 100
$caml = '<OrderBy Override="TRUE"><FieldRef Name="ID"/></OrderBy>' 
$spQuery.Query = $caml 

do
{
    $listItems = $list.GetItems($spQuery)
    $count = $listItems.Count
    $spQuery.ListItemCollectionPosition = $listItems.ListItemCollectionPosition
    $batch = "<?xml version=`"1.0`" encoding=`"UTF-8`"?><Batch>"
    $j = 0
    for ($j = 0; $j -lt $count; $j++)
    {
        $item = $listItems[$j]
        write-host "`rProcessing ID: $($item.ID) ($($j+1) of $($count))" -nonewline
        $batch += "<Method><SetList Scope=`"Request`">$($list.ID)</SetList><SetVar Name=`"ID`">$($item.ID)</SetVar><SetVar Name=`"Cmd`">Delete</SetVar><SetVar Name=`"owsfileref`">$($item.File.ServerRelativeUrl)</SetVar></Method>"
        if ($i -ge $count) { break }
    }
    $batch += "</Batch>"

    write-host

    write-host "Sending batch..."
    $result = $web.ProcessBatchData($batch)

    write-host "Emptying Recycle Bin..."
    $web.RecycleBin.DeleteAll()
}
while ($spQuery.ListItemCollectionPosition -ne $null)
$web.Dispose()

Lookup Field localization not working when using ContentTypeBinding

Whilst working with lookup fields, I noticed something strange when you separated the Fields and ContentType from your list and you try to use localization. Separating your content type and fields from your list basically means that you no longer work with a custom list definition, but you use ContentTypeBinding on a list instance to create your list.

Here’s an example on how the project is set up:

lookupFieldLocalizationProject

The red¬†arrows point to the lookup list where I use the ContentType¬†“LookUpContentType”, where a lookup field¬†is referenced. This lookup field resides in the “Fields” element:

“Fields” Elements.xml

<?<span class="hiddenSpellError">xml version="1.0" encoding="utf-8"?>
xmlns="http://schemas.microsoft.com/sharepoint/">
  <Field Name="LookUpFieldInElements"
  Required="TRUE"
  ID="{32A4FE06-2745-40D0-B92C-76A58B16C7B0}"
  Type="Lookup"
  Mult="FALSE"
  Overwrite="TRUE"
  List="Lists/Source"
  ShowField="Title"
DisplayName="$Resources:lookupfield,LookUp;"
  StaticName="LookUpFieldInElements"
  />
</Elements>

“LookUpContentType” Elements.xml

<?xml version="1.0" encoding="utf-8"?>
xmlns="http://schemas.microsoft.com/sharepoint/">
<!-- Parent <span class="hiddenSpellError" pre="Parent ">ContentType</span>: Item (0x01) -->
<ContentType ID="0x01002846E06F521348B79F631D4D9E6A07CC" Name="LookUpContentType" Group="Custom Content Types" Description="My Content Type" Inherits="TRUE" Version="0">
    <FieldRefs>
<FieldRef ID="{32A4FE06-2745-40D0-B92C-76A58B16C7B0}" Name="LookUpFieldInElements" Required="TRUE" />
    </FieldRefs>
ContentType>
</Elements>

“LookUpListCTInstance” Elements.xml

<?xml version="1.0" encoding="utf-8"?>
xmlns="http://schemas.microsoft.com/sharepoint/">
<ListInstance
Title="LookUpListCT"
    OnQuickLaunch="TRUE"
    TemplateType="100"
    Url="Lists/LookUpListCT"
Description="My List Instance with ContentTypeBinding"
    FeatureId="00bfea71-de22-43b2-a848-c05709900100">
  </ListInstance>
<ContentTypeBinding ContentTypeId="0x01002846E06F521348B79F631D4D9E6A07CC" ListUrl="Lists/LookupListCT" />
</Elements>

The blue arrows point to a regular list definition which has a list instance included.

The Schema.xml of the “LookupList” list definition

<ContentTypes>
      <ContentType ID="0x0100468fd589c1da4592929a6768ac144020" Name="ListFieldsContentType">
        <FieldRefs>
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" />
<FieldRef ID="{a51cabb2-7594-41db-8523-ea5de71e6202}" Name="LookUp" />
        </FieldRefs>
      </ContentType>
    </ContentTypes>
    <Fields>
      <Field
        Name="LookUp"
        ID="{a51cabb2-7594-41db-8523-ea5de71e6202}"
DisplayName="$Resources:lookupfield,LookUp;"
        Type="Lookup"
        List="Lists/Source"
        ShowField="Title" />

When you take a look at the DisplayNames¬†of both the LookupFields¬†residing in Schema.xml and “Fields” Elements.xml, you’ll see that the value holds “$Resources:lookupfield,LookUp;”. So we’re trying to localize the LookupField’s column name.

I’ve added the following resource files where I can add my translations:

lookupFieldLocalizationResourceFiles

lookupfield.resx

<data name="LookUp" xml:space="preserve">
  <value>LookUp</value>
</data>

lookupfield.en-US.resx

<data name="LookUp" xml:space="preserve">
  <value>[ENG] LookUp</value>
</data>

lookupfield.nl-NL.resx

<data name="LookUp" xml:space="preserve">
  <value>[NL] LookUp</value>
</data>

lookupfield.fr-FR.resx

<data name="LookUp" xml:space="preserve">
  <value>[FR] LookUp</value>
</data>

Looks great! Let’s check out the result on our SharePoint lists:

lookupFieldLocalizationDutchLocalization

lookupFieldLocalizationDutchLocalizationCT

Apparently¬†there’s a bug when separating your LookupField¬†from your Schema.xml where it will only use the English localization. So whenever you want to use LookupFields¬†in custom lists, use the list definition way!

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
                    }
                }
            }
        }

Updating a list item field using its internal name

While looking up examples of eventreceivers for updating list item fields, I noticed a high usage of display names. List fields were often referenced using their display name, like:

properties.ListItem["Title"] = "something";

It works, but, when there are multiple languages used on the SharePoint site, it might get broken. A solution to this is using the internal field name. This can be obtained by GetFieldByInternalName(). By using the internal field name, SharePoint will never be confused by changes. Imagine someone changing the name of a column and breaking your code. That’s not good. The internal field name will always stay the same, unless someone changes it programmatically of course ūüėČ

I used the following code snippet to add text to the title of a list item:

       public override void ItemAdded(SPItemEventProperties properties)
       {
           base.ItemAdded(properties);
           Guid oId = properties.ListItem.Fields.GetFieldByInternalName("Title").Id;
           properties.ListItem[oId] += " and something added to the title field";
           properties.ListItem.Update();
       }

Finding the internal field name is pretty simple. By using Visual Studio 2010’s server explorer, we can access almost all the information of our SharePoint site.

Open up the Server Explorer by going to view and clicking the Server Explorer:

open server explorer

With the Server Explorer view open click on the Add SharePoint Connection button and add your site:

add sharepoint connection

Now we have access to our site properties within Visual Studio 2010. Please note that everything here is read only.

Expand the Explorer View to a desired list field and click on it:

server explorer expanded

The Properties window will now show the associated properties of the field. Here we can find back the internal field name:

list field properties

Other useful information like the GUID of the field can also be found here.