When a user adds a document to a library for the first time, but omits to check in that file that information risks getting lost. This can happen for a variety of reasons such as missing mandatory fields or simple forgetfulness. The user who added the file can see it fine, and won't know that anything is wrong. Unfortunately, until it's checked in for the first time other users and even the Site Collection Admin can't see it. Since SharePoint is all about sharing information, this is bad.

If you're a site owner you can manage these files. Document Library settings has an option called 'Manage files which have no checked in version'. This shows you which files have the issue, and allows you to take control of the files so they can be checked in. There's a few issues with this alas;

  1. You can't sort or filter the list of files
  2. You can't export the list except a clumsy copy and paste of the page HTML
  3. Someone has to remember to check for 'lost' files
  4. No way to notify users automatically

Luckily, there's an easy way to get the list of 'lost' items via the API. So I set about creating an automatic notification. The following powershell calls the CheckedOutFiles property of the library, puts the results into a CSV and emails it to the support folks so they can gently explain to users what happened and correct it.

Actually though as you'll see from the documentation here there are several properties and methods to play with. You could easily use the CheckedOutByEmail property to email users directly, or the TakeOverCheckOut method to checkin the file sautomatically instead.

I saw some commentary online stating that the CheckedOutFiles property only yields results when the library has Force Checkout enabled. As far as I can tell this is rubbish as my library doesn't have force checkout and the script works fine.

To deploy this I placed it in a secure folder on the server and used Task Scheduler to execute it once a week. Simple and perfect!

Script below. Happy SharePointing!

$ErrorActionPreference = "Stop"
$ver = $host | select version 
if($Ver.version.major -gt 1) {$Host.Runspace.ThreadOptions = "ReuseThread"} 
if(!(Get-PSSnapin Microsoft.SharePoint.PowerShell -ea 0)) 
{ 
Write-Progress -Activity "Loading Modules" -Status "Loading Microsoft.SharePoint.PowerShell" 
Add-PSSnapin Microsoft.SharePoint.PowerShell 
} 
 
$SourceWebURL = "http://MyDomain/sites/ProposalDocs" 
$SourceLibraryTitle = "Proposal Documents" 
$file = 'C:\Powershell\QATools\CheckedOutFiles.csv'

$sWeb = Get-SPWeb $SourceWebURL 
$sList = $sWeb.Lists | ? {$_.Title -eq $SourceLibraryTitle} 

$InvisibleFiles = $sList.CheckedOutFiles

foreach ($InvisibleFile in $InvisibleFiles)
{
	$out = "$($InvisibleFile.CheckedOutBy),$($InvisibleFile.Url)"
	$out | Add-Content $file
}

$smtpserver="smtp.mydomain.com" 
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$att = new-object Net.Mail.Attachment($file)
$msg = new-object Net.Mail.MailMessage
$msg.To.Add("mySupportUser@MyDomain.com")
$msg.To.Add("myOtherSupportUser@MyDomain.com")

$msg.From = "noreply@MyDomain.com" 
$msg.Subject = "Checked Out Items" 
$msg.Body = "Attached is the Checked Out Files report."

$smtp=new-object Net.Mail.SmtpClient($smtpServer) 
$msg.Attachments.Add($att)
$smtp.Send($msg)

$att.Dispose()

Remove-Item $file