This item cannot be updated because it is locked as read-only

Had to call in Microsoft Support to help unlock files that were marked as read-only. Some files appeared to be locked, but using the UI, they couldn’t be unlocked, declared as a record, or edited.

Error: This item cannot be updated because it is locked as read-only
Error: The file “your file string” is checked out for editing by SHAREPOINT\system

This script was setup to run on a SharePoint 2010 instance. If you need to run it on another version, try updating the $sharePointAssembly line.
To unlock a file, input the URL of the file in the line that starts with $fileUrl.


$recordFields =
  "_vti_ItemHoldRecordStatus",
  "_vti_ItemDeclaredRecord"

$recordProperties =
  "ecm_RecordRestrictions",
  "ecm_ItemLockHolders",
  "ecm_ItemDeleteBlockHolders"

$fileUrl = "http://siteURL/siteCollection/libraryName/folder/subFolder/fileName.pdf"

$sharePointAssembly = [System.Reflection.Assembly]::Load("Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c")

Add-Type -TypeDefinition @"
using Microsoft.SharePoint;

public class EventsDisabler : SPEventReceiverBase
{
  public EventsDisabler() {}
  
  public bool EventsDisabled
  {
    get { return !EventFiringEnabled; }
    set { EventFiringEnabled = !value; }
  }
}
"@ -ReferencedAssemblies $sharePointAssembly

Write-Host "Getting site collection at $fileUrl..."
[Microsoft.SharePoint.SPSite]$site = New-Object Microsoft.SharePoint.SPSite($fileUrl)
if ($site -eq $null) { exit }
$siteUrl = $site.Url
Write-Host "Found site collection $siteUrl"
Write-Host ""

Write-Host "Getting web at $fileUrl..."
[Microsoft.SharePoint.SPWeb]$web = $site.OpenWeb()
if ($web -eq $null) { exit }
$webTitle = $web.Title
Write-Host "Found web $webTitle"
Write-Host ""

Write-Host "Verifying current user is System Account"
$web.CurrentUser.ID
$site.SystemAccount.ID
if ($web.CurrentUser.ID -ne $site.SystemAccount.ID)
{
  Write-Error "Please run this script as System Account" -Category PermissionDenied
  #exit
}
Write-Host ""

Write-Host "Getting list at $fileUrl..."
Write-Host $fileUrl
[Microsoft.SharePoint.SPList]$list = $web.GetList($fileUrl)
if ($list -eq $null) { exit }
$listTitle = $list.Title
Write-Host "Found list $listTitle"
Write-Host ""

Write-Host "Getting list item at $fileUrl..."
[Microsoft.SharePoint.SPListItem]$listItem = $web.GetListItem($fileUrl)
if ($listItem -eq $null) { exit }
$listItemName = $listItem.Name
Write-Host "Found list item $listItemName"
Write-Host ""

$eventsDisabler = New-Object EventsDisabler
$eventsOriginallyDisabled = $eventsDisabler.EventsDisabled
if ($eventsOriginallyDisabled -eq $false)
{
  Write-Host "Disabling events"
  $eventsDisabler.EventsDisabled = $true
  Write-Host ""
}

$didWork = $false
$itemNeedsUpdate = $false

#Discard any check-out
if ($listItem.File -ne $null -and $listItem.File.CheckOutType -ne [Microsoft.SharePoint.SPFile+SPCheckOutType]::None)
{
  Write-Host "Undoing check-out"
  $listItem.File.UndoCheckOut()
  $didWork = $true
}
else
{
  Write-Host "No file or file is not checked out"
  Write-Host ""
}

#Iterate the Record fields and set all values to null
foreach($recordField in $recordFields)
{
  if ($listItem.Fields.ContainsField($recordField) -eq $true -and $listItem[$recordField] -ne $null)
  {
    $recordFieldValue = $listItem[$recordField]
    Write-Host "$recordField = $recordFieldValue"
    Write-Host "Setting $recordField to null"
    $listItem[$recordField] = $null
    $didWork = $true
    $itemNeedsUpdate = $true
  }
}

#Iterate the Record properties and remove any that exist
foreach($recordProperty in $recordProperties)
{
  if ($listItem.Properties.ContainsKey($recordProperty) -eq $true)
  {
    $recordPropertyValue = $listItem.Properties[$recordProperty]
    Write-Host "$recordProperty = $recordPropertyValue"
    Write-Host "Removing property $recordProperty"
    $listItem.Properties.Remove($recordProperty)
    $didWork = $true
    $itemNeedsUpdate = $true
  }
}

#Remove the icon Record lock overlay
if ($listItem.IconOverlay -eq "lockoverlay.png")
{
  Write-Host "Removing the icon Record lock overlay"
  $listItem.IconOverlay = $null
  $didWork = $true
  $itemNeedsUpdate = $true
}

if ($didWork -ne $true)
{
  Write-Host "No changes were made"
}
Write-Host ""

#Update the item
if ($itemNeedsUpdate -eq $true)
{  
  Write-Host "Updating item"
  $listItem.SystemUpdate()
  
  Write-Host ""
}

if ($eventsOriginallyDisabled -ne $true)
{
  Write-Host "Enabling events"
  $eventsDisabler.EventsDisabled = $false
  Write-Host ""
}

$web.Dispose()
$site.Dispose()

Find all Lists and Libraries Where InfoPath is Used

Recently had the need to find and update most every InfoPath form in a production farm. This, mixed with the future demise of InfoPath, prompted the need to find or write a script. The below script will traverse every list and library in a given web app. The script can be easily modified to include every web app in a farm.

if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) 
{
                Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}

$fileName = "C:\xsn-" + $(Get-Date -Format "yyyyMMddHHmmss") + ".csv"

$webApp = Get-SPWebApplication "http://webApp.sharepointed.com" 
"Title `t URL `t Type" | out-file $fileName

$(foreach($siteColl in $webApp.Sites)
{
                foreach($web in $siteColl.AllWebs)
                {
                foreach($list in $web.Lists) {                        
                
                                if ($list.BaseType -eq "DocumentLibrary" -and $list.BaseTemplate -eq "XMLForm"){
                                                "$($list.title) `t $($list.parentweb.url)$($list.DefaultViewUrl) `t Library" | out-file $fileName -Append 
                                                                
                                                
                                }elseif ($list.ContentTypes[0].ResourceFolder.Properties["_ipfs_infopathenabled"]){
                                
                                                "$($list.title) `t $($list.ParentWeb.URL)$($list.DefaultViewUrl) `t List" | out-file $fileName -Append
                                                
                                                }
												else{
												$check = 0
								foreach($c in $list.ContentTypes) {
												if($c.DocumentTemplateUrl.ToString().EndsWith("xsn") -eq $true)
												{
													$check++			
												}
												}
												if($check -gt 0)
												{
													"$($list.title) `t $($list.ParentWeb.URL)$($list.DefaultViewUrl) `t List" | out-file $fileName -Append
												}
												}
												}}})

PowerShell Unable to index into an object of type Microsoft.SharePoint.SPList.

Made a change to an existing PowerShell script, then ran into this error: Unable to index into an object of type Microsoft.SharePoint.SPList

If ((Get-PSSnapIn -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{ Add-PSSnapIn -Name Microsoft.SharePoint.PowerShell }

$web = Get-SPWeb "http://sharepointed.com/sitename"
$listTask = $web.Lists["My List"]
$sItem = $listTask.GetItemById(9)

$title = $listTask["Title"]

In the script, you will notice that I’m trying to capture the title of the item. The above script wasn’t working because I failed work with this list item, instead I was trying to work with the list object.

Correct script:

If ((Get-PSSnapIn -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{ Add-PSSnapIn -Name Microsoft.SharePoint.PowerShell }

$web = Get-SPWeb "http://sharepointed.com/sitename"
$listTask = $web.Lists["My List"]
$sItem = $listTask.GetItemById(9)

$title = $sItem["Title"]

Convert yyyyMMdd string to date and time using PowerShell

 

How do you convert an input string to a date and time format.


[System.Globalization.CultureInfo]$provider = [System.Globalization.CultureInfo]::InvariantCulture
$sInput = 19991231
$format = "yyyyMMdd"
[ref]$parsed = get-date
[DateTime]::TryParseExact($sInput, $format, $provider, [System.Globalization.DateTimeStyles]::None, $parsed)

$parsed is the returned value.

Person or Group Field Append or Remove a Group

Using PowerShell I needed to append a group to a Person or Group field in a list.  The same logic should apply for adding or removing a user.

Append Group:



$web = Get-SPweb "http://sharepointed.com/sites/taco/"
$list = $web.lists["Good Tacos"]

$groupName = "Taco Eaters"
$group = $web.SiteGroups[$groupName]
$GroupValue = new-Object Microsoft.SharePoint.SPFieldUserValue($web,$group.id, $group.Name)

foreach ($item in $list.items)
 {
 $groups = $item["GroupsField"]

$groups.Add($GroupValue)

$item["GroupsField"] = $groups
 $item.Update()
 }

Remove Group:



$web = Get-SPweb "http://sharepointed.com/sites/taco/"
$list = $web.lists["Good Tacos"]

$groupName = "Taco Eaters"
$group = $web.SiteGroups[$groupName]
$GroupValue = new-Object Microsoft.SharePoint.SPFieldUserValue($web,$group.id, $group.Name)

foreach ($item in $list.items)
{
$groups = $item["GroupsField"]

<span style="line-height: 1.5em;">$groupRemove = $groups | ? { $_.LookupId -eq $GroupValue.LookupId }</span>

$groups.Remove($groupRemove)

$item["GroupsField"] = $groups
$item.Update()
}

Update users display name in SharePoint

The script below will allow you to change a users display name in SharePoint.


# add SharePoint snap-in if needed

 	$ntName = "domain\XXX"
	$displayName = "New Name"
 	$webApp = "http://webapp.sharepointed.com/"
	$user = Get-SPUser -web $webApp -Identity $ntName
	
	Get-SPWebApplication $webApp | Get-SPSite -Limit ALL | Get-SPWeb -Limit ALL | ForEach-Object {
		Set-SPUser -Identity $user -DisplayName $displayName -Web $_.URL
	}

Cleaning up a drop off library mess

You installed SharePoint, then your power users enabled the content organizer feature all over your farm. This isn’t necessarily a bad thing, but if it’s not being used, turn it off! Less load on the farm, the better off you will be.

You will find plenty of examples on the web about cleaning up this mess, but I took a slightly different approach. Before disabling the feature and deleting the library, I first checked to make sure the library was empty and no content org rules had been created.

Start-SPAssignment -Global

$web = Get-SPWeb "http://webapp.sharepointed.com"
$feature = Get-SPFeature "7ad5272a-2694-4349-953e-ea5ef290e97c" 

   if ($web.Features[$feature.ID]) 
   {
            $corList = $web.lists["Content Organizer Rules"]
            $dolList = $web.lists["Drop Off Library"]
            
            #check for Cont Org Rules and items in Drop Off Library
            if(($corList.ItemCount -eq 0) -and ($dolList.ItemCount -eq 0))
            {
                  #disable the feature
                  Disable-SPFeature -Identity "7ad5272a-2694-4349-953e-ea5ef290e97c" -Url $_.URL -Confirm:$false
                  
                  #remove the Drop Off Library
                  $dolList.AllowDeletion = $true
                  $dolList.Update()
                  $dolList.Delete()
            }
   } 

Stop-SPAssignment -Global

Use PowerShell to Create Year and Month Folders in Library

How do you create a year and month folder structure in a SharePoint document library?

Every year, I needed to create a folder structure in a report library that looks like this:

Next Year – 01 …… 12

Using PowerShell I’m able to create the parent folder (year), then create the subfolders (months).

*NOTE*
This can be executed using a scheduled task or using a workflow in SharePoint Designer with the help of the iLoveSharePoint add in.

if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}

$spWeb = Get-SPWeb "http://sharepointed.com/sites/reports"
$spDocLib = $spWeb.Lists["TacoReports"]

$yearFolder = (get-date (Get-Date).AddYears(1) -UFormat "%Y")

$targetFolder = $spWeb.GetFolder($spDocLib.RootFolder.ServerRelativeUrl + "/$yearFolder")

if($targetFolder.Exists -eq $false)
{
    $spFolder = $spDocLib.AddItem("",[Microsoft.SharePoint.SPFileSystemObjectType]::Folder,$yearFolder)
	$spFolder.Update()

	$i = 1
	While ($i -le 12)
	{
		$subFolder= "{0:D2}" -f $i
		$spSubFolder = $spDocLib.AddItem($spFolder.Folder.ServerRelativeUrl,[Microsoft.SharePoint.SPFileSystemObjectType]::Folder,$subFolder)
		$spSubFolder.Update()

		$i++
	}
}

Get the current year and add one.
Check if the new folder already exist.
Create the year folder.
Then create the twelve subfolders (one for each month).

Change List or Library URL and Name

Recently had a scenario where I needed to move a bunch of libraries from different sites, to another farm. Every library was titled the same, so when I went to import the libraries, I ran into a small issue. Using a combo of Export-SPweb and Import-SPweb, I moved a library over, then updated the library url and name to the source site name.

if ((Get-PSSnapin “Microsoft.SharePoint.PowerShell” -ErrorAction SilentlyContinue) -eq $null)
{
Add-PSSnapin “Microsoft.SharePoint.PowerShell”
}

$webURL = “http://sharepointed.com/sites/taco”

$before = “LibBefore”
$after = “LibAfter”

#Change the URL of the library
$web = Get-SPWeb $webURL
$web.lists[$before].RootFolder.MoveTo($after)
$Web.Dispose()

#Update the library name
$web = Get-SPWeb $webURL
$list = $web.Lists[$after]
$list.Title = $after
$list.Update()
$web.Dispose()

Couple notes.
You might be able to shorten the script to rename and update in a couple lines of code.
If you use the script to change the URL of a list, the list will moved to the root of the site. it will no longer live at site/lists.

*I have not tested this with libraries or list that workflows associated with them. It’s also safe to assume any links to documents would be broken.*

One or more field types are not installed properly. Go to the list settings page to delete these fields

Error:
One or more field types are not installed properly. Go to the list settings page to delete these fields.

After much research, I have found that this error can be the result of a couple things.
1. Programmatically create a site column / content type and the process fails. The result is a broken column that you have to hunt down and remove from the content database.
2. Using a combination of Export-SP and Import-SPweb, an imported list or library will display the error.

In my case, #2 needed to be fixed. Basically, I had a lookup column that had become orphaned from its parent list.
When you look at the field settings (Library/List Settings –> click on your lookup column) you will see that the Get information from: property is empty.

So how do you reconnect or refresh the lookup field property?
Using PowerShell, the fields web ID and source list ID schemaXML need to be populated.

if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null)
{
Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
$web = Get-SPWeb "http://sharepoint.com/site/"
$list = $web.Lists["Broken LoopField List"]
$lookupList = $web.Lists["Loopup Source List"]

$fixColumn = $list.Fields["My Broken Lookup Field"]

#Change schema XML on the lookup column
$fixColumn.SchemaXml = $fixColumn.SchemaXml.Replace($fixColumn.LookupWebId.ToString(), $web.ID.ToString())
$fixColumn.SchemaXml = $fixColumn.SchemaXml.Replace($fixColumn.LookupList.ToString(), $lookupList.ID.ToString())
$fixColumn.Update()

What’s going on here?
Load the SharePoint snapin.
Get site that holds the broken list / library.
Get broken list / library.
Get source list / library that the broken lookup references.
Get broken field.
Update the broken field schemaXML.