SharePoint 2010 Cross-Site Lookup using PowerShell

A common SharePoint request is to create a lookup field from a list in another site. This is possible so long as the sites are within the same site collection, but it does require some customization.

Since I am dealing with a single-server farm, I was able to do this fairly quickly in PowerShell:

# Set the site collection in which the source and destination webs exist
$site = Get-SPSite("https://mysite/path/")

# Set the source web to the web where the source list exists
$sourceweb = $site.OpenWeb("sourcepath")

# Set the destination web to the web where you wish to add the lookup
$destinationweb = $site.OpenWeb("destinationpath")

# Set the source list to the list in which the field exists
$sourcelist = $sourceweb.Lists["List Name"]

# Set the source field to the field you wish to perform lookups on
$sourcefield = $sourcelist.Fields["Field Name"]

# Get the collection of all fields in the destination list
$destinationfields = $destinationweb.Lists["List Name"].Fields

# Add a lookup field to the fields collection
# See the SPFieldCollection AddLookup(String,Guid,Guid,Boolean) method
# For details: http://msdn.microsoft.com/en-us/library/ms430950.aspx
$lookupname = $destinationfields.AddLookup($sourcefield.Title,$sourcelist.ID,$sourceweb.ID,false)
# The above produces an error: Missing expression after ','. At line:1, char:83
# Right--in Powershell, $false is False.
$lookupname = $destinationfields.AddLookup($sourcefield.Title,$sourcelist.ID,$sourceweb.ID,$false)

# Get the newly-added lookup field
$lookup = $destinationfields.GetFieldByInternalName($lookupname)

# Set the lookup field to the internal name of the source field
$lookup.LookupField = $sourcefield.InternalName

# Update the lookup -- without this the previous changes won't be saved
$lookup.Update()

# Dispose of the SPWeb and SPSite objects
$sourceweb.Dispose()
$destinationweb.Dispose()
$site.Dispose()

Using a list or array as a PowerShell script input parameter

One of my colleagues created a PowerShell script that we use to migrate SharePoint 2010 sites from the SharePoint 2007 interface (UI 3) to the SharePoint 2010 interface (UI 4). The script works rather well for updating one or two sites at a time:

Set-UserInterface2010.ps1 -url "https://my.sharepoint.site/path/"

Today I received a request to update a list of 32 sites. After updating one, I thought–this is going to be tedious. We can improve this.

First, I updated the parameter to accept an array of strings instead of a single string.

Before
param([string]$url = $(throw "Please specify a Site URL to convert to the SP2010 look and feel"))

After
param([string[]]$url = $(throw "Please specify a Site URL to convert to the SP2010 look and feel"))

Next, I wrapped the call to the main function in a ForEach loop:
ForEach ( $siteurl in $url ) {
...
# Throw in a line break to separate output
Write-Host ("`n`n")
}

The script can still take a single site as input (the string is treated as a string array with a single element), but now I can also pass it a list:

Set-UserInterface2010.ps1 -url "https://my.sharepoint.site/path1/","https://my.sharepoint.site/path2/","https://my.sharepoint.site/path3/"

Here’s hoping that taking a couple minutes to update the script and running it once saved me more time than running it 32 times!

SharePoint error when editing a wiki page: Sys.WebForms.PageRequestManagerServerException: An unexpected error has occurred

I’ve had an ongoing problem where some users, when clicking the Edit Page button on a SharePoint 2010 wiki page, get the following error:

Sys.WebForms.PageRequestManagerServerException: An unexpected error has occurred

I found a couple blog posts about this suggesting a permissions issue (which made sense, as users in other groups could successfully edit pages):

Both sites suggested that an Access Denied message should appear in the ULS logs. However, no such message appeared in relation to my errors.

ULS entry:
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.SharePoint.Publishing.WebControls.MediaWebPart.get_WebPartAdderId()
at Microsoft.SharePoint.Publishing.WebControls.InsertMediaRibbonButton.RegisterRequiredScripts()
at Microsoft.SharePoint.Publishing.WebControls.InsertMediaRibbonButton.OnPreRender(EventArgs e)
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint... 1317dffd-b607-4f1c-9145-b4adc1d364ba

The MediaWebPart. SharePoint 2010 added a video/audio insertion feature on the ribbon, and the error occurred when trying to add the button.

It turns out, the permissions to the Web Part Gallery had been customized (i.e. broken inheritance from the parent site collection), and some groups who had permission to edit wiki pages did not have read permission for the Web Part Gallery (and, consequently, the MediaWebPart). Adding Restricted Read access for the group in question (to either the MediaWebPart or to the Web Part Gallery) solved this problem.

I don’t imagine this will be a common problem for other people, but I thought it worth mentioning.

Displaying numeric fields in SharePoint 2010 without commas

A common request is to display number fields in SharePoint 2010 lists without commas. For example, if you had a list of books that included PublicationYear column, it would format the values as:

  • 2,001
  • 1,998
  • 2,011

This is confusing to the viewer, as values no longer look like years.

The most frequent suggestion is to create an additional field that calculates a value based on the Number field and ignores all non-numeric parts. This is relatively straightforward, and can be accomplished entirely within the browser:

=TEXT([MyNumericData], "0")

Or, in a case where you’d like to retain two digits after the decimal:
=TEXT([MyNumericData], "0.00")

SharePoint appears to store the data in its numeric format in a another column, hidden to the browser but visible in SharePoint Designer. It’s [column name] followed by a period. For example, I created a field called MyNumericData, so the hidden field is MyNumericData. Again:

  • MyNumericData
  • MyNumericData.

Adding this to a view via SharePoint Designer is non-obvious. Here are the steps I used:

  1. Click on a data item in Design mode until you see a tab labeled xsl:value-of:
    SharePoint Designer 2010 Design Mode
  2. Right-click the item and select Edit Formula
  3. Remove the current XPath expression
  4. Add the MyNumericData. row from the fields pane
  5. Add the format-number function from the Math / Number functions
  6. Add the appropriate format pattern (e.g. ‘#’)

The appropriate format pattern depends on how you wish to display the data:

  • Display integer value:
    format-number($thisNode/@MyNumericData.,'#')
  • Always display two digits after the decimal:
    format-number($thisNode/@MyNumericData.,'0.00')
  • Display up to 2 digits after the decimal:
    format-number($thisNode/@MyNumericData.,'#.##')

If you look at the code view after making these changes, you will see that SharePoint Designer added a hefty chunk of XSL to the XsltListViewWebPart.

Is there any advantage to this method over adding a calculated field? Not as far as I can tell–but it’s interesting to note that there is more than one overly-complicated solution to what seems like a trivial problem.

Updating a SharePoint page’s Page Layout using PowerShell

Earlier today I tried to update the page layout on a SharePoint 2010 site via the browser, which produced an error. I looked up the error’s correlation ID in the ULS logs and found this:
GetFileFromUrl: ArgumentException when attempting get file Url https://oldsitename/webname/_catalogs/masterpage/Block.aspx Value does not fall within the expected range.

I couldn’t change the page layout via the browser. I couldn’t change the page layout via SharePoint Designer.

Fortunately, I found Patrick Lamber’s helpful post, How to programmatically change the page template in Moss 2007, which basically described the same error and described applying a new page layout via the object model.

For whatever reason, I thought I could do it just as easily via PowerShell. I started following Jason Grimme’s post, Update SharePoint List Item(s) with Powershell, but I was unable to check-out/check-in the page or access the PublishingPageLayout property.

Liam Cleary’s reply on a TechNet post (Using PowerShell to Checkout and Checkin a file) tipped me off that I needed to use SPFile (rather than SPListItem) in order to perform the necessary operations. Here are the commands I ended up using:

$spWeb = Get-SPWeb("http://currentsitename/webname")
$spFile = $spWeb.GetFile("http://currentsitename/webname/Pages/Test-Page.aspx")
$spFile.CheckOut("Online",$null)
$spFile.Properties["PublishingPageLayout"] = "/_catalogs/masterpage/Block.aspx, Block"
$spFile.Update()
$spFile.CheckIn("Update page layout via PowerShell",[Microsoft.SharePoint.SPCheckinType]::MajorCheckIn)
$spWeb.Dispose()

I visited the page and…another error! This one telling me there wasn’t a valid page layout. Funny, it worked in development.

It turns out, when I compared it to the PublishingPageLayout property of a working page, I had missed the space character after the comma.

Doesn’t work:
"/_catalogs/masterpage/Block.aspx,Block"

Works:
"/_catalogs/masterpage/Block.aspx, Block"

Once I applied the proper path, I was also able to change the page layout via the browser again.