Wednesday, 24 November 2010

Configuring list RSS feeds in SharePoint using PowerShell

In my previous article, I covered how to enable and configure RSS feeds at the site level with PowerShell, including disabling list RSS feeds by default so that you can choose which lists you want to enable when required.

In this article, I will go through how to enable RSS feeds on a list and configure the various options associated with them, including selecting the list columns to be published in the feed.

 

Enabling RSS Feeds on a list

The first part of the script connects to the site and list you want to configure:

#Get site collection and list objects
$web = Get-SPWeb http://portal
$list = $web.Lists["Documents"]

We then enable RSS feeds on the list, if currently disabled. This is the same thing as clicking on the following option in the browser UI:

image

if ($list.EnableSyndication -eq $false)
{
    $list.EnableSyndication = $true
    $list.Update()
    write-host "RSS Feeds enabled on list:" $list.Title
}
else
{
    write-host "RSS Feeds already enabled on list:" $list.Title
}

 

List RSS properties

For configuring the properties of the RSS feed, we need to use the EnsureRssSettings method to check the RSS settings of the list and connect to the root folder:

#Checks the RSS settings of the list, and, if necessary, updates them to ensure that the most current settings are used
$list.EnsureRssSettings()
#Get root folder which contains RSS configuration properties
$rootFolder = $list.RootFolder

The commands for setting the properties themselves depends on the options you wish to set with the script. The table below shows the code for setting each of the properties along with the section of the UI they correspond to:

RSS Channel Information image
RSS Channel settings can be set with the following code. Where there is a Yes/No option, as with the truncate multi-line text setting above, use 0 to specify No and 1 to specify Yes:

$rootFolder.Properties["vti_rss_LimitDescriptionLength"] = 0
$rootFolder.Properties["vti_rss_ChannelTitle"] = "Intranet: Documents"
$rootFolder.Properties["vti_rss_ChannelDescription"] = "RSS feed for the Documents list."
$rootFolder.Properties["vti_rss_ChannelImageUrl"] = "/_layouts/images/siteIcon.png"

Document Options (document libraries only) image
$rootFolder.Properties["vti_rss_DocumentAsLink"] = 1
$rootFolder.Properties["vti_rss_DocumentAsEnclosure"] = 1
Item Limit image
$rootFolder.Properties["vti_rss_ItemLimit"] = 25
$rootFolder.Properties["vti_rss_DayLimit"] = 7
Other Options (not configurable from the UI) These options are hidden from the UI and only configurable through code/PowerShell. I haven’t tested either of them, but assume the first specifies if you want to display the RSS icon with the feed (default Yes) and the second configures the RSS feed for display on the Quick Launch menu (default No):

$rootFolder.Properties["vti_rss_DisplayRssIcon"] = 1
$rootFolder.Properties["vti_rss_DisplayOnQuicklaunch"] = 0

Of course, as the option to configure these settings are both hidden from the UI, they may not do anything at all!

Once you have configured the properties of the list RSS feed, you have to apply them with the following update commands:

#Update root folder and list with the changes
$rootFolder.Update()
$list.Update()

 

Selecting list columns for use in RSS Feeds

The PowerShell script code in this section will include the following list columns for display in the RSS feed:

image

Columns marked with an asterisk will map to standard RSS tags – e.g., "Created by" is mapped to the RSS "Author" tag. All other columns will display in the RSS description tag.

First, we need to get a hidden view called “RssView” and delete all currently selected columns from the view:

#Get RSS View and delete all existing columns
$rssView = $list.Views["RssView"]
$rssViewFields = $rssView.ViewFields
$rssViewFields.DeleteAll()

Next, we add the columns to the view as required.

#Add new columns using their display name
$rssViewFields.Add("Title")
$rssViewFields.Add("Version")
$rssViewFields.Add("Modified By")

The method I have used above allows you to specify the display name of the columns to be added. You could choose to use internal column names instead with this code for each column: $rssViewFields.Add($list.Fields.GetFieldByInternalName("fieldname"))

Finally, we have to update the RSS view and dispose of the web object:

#Update the RSS View
$rssView.Update()

#Dispose of web object
$web.Dispose()

Monday, 22 November 2010

Enabling and configuring SharePoint RSS feeds at the site level with PowerShell

When you create a new site collection in SharePoint 2010, the option to publish lists as RSS feeds may be switched off by default, depending on the site template used. It is easy enough to switch on for the site collection by going to Site Actions > Site Settings > RSS in the UI and ticking the Allow RSS feeds in this site collection checkbox.

image

Once this option is selected, the Allow RSS feeds in this site option is then also enabled for each site in the site collection. So, that’s easy right? Well, yes, but there are still a couple of things to consider:

  • Once these site options are selected, RSS feeds are automatically enabled for every list in each site – What if you want to leave RSS feeds on lists disabled and only enable the lists you wish to publish?
  • The Advanced Settings shown in the screenshot above apply on a site-by-site basis. If you wanted to set the same channel elements across all sites in the site collection, then this would mean going to each site, accessing the RSS settings, and typing in the same Advanced Settings every time. Not bad if you only have three sites, but very time consuming if you have 100’s!

The following PowerShell script performs the following tasks to try and ease the pain:

  • Enables RSS feeds on the site collection
  • Enables RSS feeds on each site in the site collection
  • Configures the Advanced Settings on each site in the site collection
  • Disable RSS feeds on each list in each site in the site collection

#Get site collection object
$site = Get-SPSite -Identity http://portal

#Enable RSS feeds in the site collection
$site.SyndicationEnabled = $true

#Step through each site in the site collection
$site | Get-SPWeb -Limit All | ForEach-Object {

    #Enable RSS feeds on this site
    $_.SyndicationEnabled = $true

    #Change properties exposed on the RSS administration page
    $_.AllProperties["vti_rss_Copyright"] = "Get-SPScripts"
    $_.AllProperties["vti_rss_ManagingEditor"] = "Phil Childs"
    $_.AllProperties["vti_rss_WebMaster"] = ""
    $_.AllProperties["vti_rss_TimeToLive"] = 60

    #Set FeedFormat property as configured by RSS administration page
    $_.AllProperties["vti_rss_FeedFormat"] = "Rss20"

    #Update site object and post message to console
    $_.Update()
    write-host "Changed RSS settings on site:" $_.Title
   
    #Disable RSS Feeds on all lists – remove this if/else statement if you want to leave them enabled
    $_.Lists | ForEach-Object {
   
        if ($_.EnableSyndication –eq $true)
        {
            $_.EnableSyndication = $false
            $_.Update()
            write-host "RSS Feeds disabled on list:" $_.Title
        }
        else
        {
            write-host "RSS Feeds already disabled on list:" $_.Title
        }
    }
}

#Dispose of site object
$site.Dispose()

One item to note here is the $_.AllProperties["vti_rss_FeedFormat"] = "Rss20" line. This must set the RSS version to 2.0, but there is no option in the interface to change this – it gets added automatically by SharePoint when you configure the site RSS feed settings in the UI, so I have included it in the script to emulate the same behaviour.

If you’re wondering how to use PowerShell for configuring RSS settings on lists, I’ll be covering this in a future post soon…

Friday, 19 November 2010

Republish all hub content types in SharePoint using PowerShell

The idea for this script came in as a request from an anonymous comment I received yesterday on an earlier post showing how to push changes from a content type hub site to site collections using PowerShell.

The script in this article steps through each content type in a content type hub and republishes any that have been previously published. Once you specify the content type hub URL, the script performs the following tasks:

  • Checks to see if the URL specified is a content type hub site
  • Enumerates through each site content type in the hub and:
    • Checks if the content type has been previously published
    • If it has, republish it and write a success message to the console
    • If it hasn’t, write a message to the console confirming it is not a published content type

To use it, run this script first:

function Republish-HubContentTypes ($CTHubURL)
{
    #Get Content Type site and web objects
    $ctHubSite = Get-SPSite $CTHubURL
    $ctHubWeb = $ctHubSite.RootWeb

    #Check the site is a content type hub
    if ([Microsoft.SharePoint.Taxonomy.ContentTypeSync.ContentTypePublisher]::IsContentTypeSharingEnabled($ctHubSite))
    {
        #Set up ContentTypePublisher object to allow publishing through the Content Type Hub site
        $spCTPublish = New-Object Microsoft.SharePoint.Taxonomy.ContentTypeSync.ContentTypePublisher ($ctHubSite)
       
        #Step through each content type in the content type hub
        $ctHubWeb.ContentTypes | Sort-Object Name | ForEach-Object {
           
            #Has the content type been published?
            if ($spCTPublish.IsPublished($_))
            {
                #Republish content type
                $spCTPublish.Publish($_)
                write-host "Content type" $_.Name "has been republished" -foregroundcolor Green
            }
            else
            {
                write-host "Content type" $_.Name "is not a published content type"
            }
        }
    }
    else
    {
        write-host $CTHubURL "is not a content type hub site"
    }
    #Dispose of site and web objects
    $ctHubWeb.Dispose()
    $ctHubSite.Dispose()
}

Once run, you can call the script by typing the following command:

Republish-HubContentTypes –CTHubURL <ContentTypeHubURL>

For example, type the following command to republish all content types in a hub site with the URL http://portal/sites/CTHub:

Republish-HubContentTypes -CTHubURL http://portal/sites/CTHub

An example output from the script is shown below:

image

Tuesday, 9 November 2010

Add and remove site content types from SharePoint lists using PowerShell

If you’ve ever been involved in a content type redesign, you’ll know how much of a pain it can be to add new site content types on a large number of existing lists, as well as removing those content types no longer needed. To try and ease the pain, in this article I have provided some example scripts on how to bulk add and remove site content types from lists using PowerShell.

Note that the scripts do not create the site content types for you – they assume that the content types are already created in the root site of the site collection containing the lists to be modified.

I have provided scripts based on a couple of scenarios:

1) Checking each site in a site collection and changing content types associated on lists with the same name – e.g., add or remove a specific content type from all document libraries called “Shared Documents”

2) Checking each site in a site collection and changing content types based on the content types already associated on lists – e.g., add or remove a content type from any document library which currently includes the “Sales Document” content type

Adding content types to lists

The first content type adding script performs these tasks:

  • Sets up variables for connecting to the site collection and specifying the name of the document library to examine in each site 
  • Uses a piped command to walk through each site in the site collection and:
    • Attach to the document library specified in the $lookForList variable (“Shared Documents” in the example script below)
    • Enable the “Allow management of content types” option on the list (you can remove this option if already set on the document library)
    • Add the “Sales Document” and “IT Document” content types to the document library
    • Output progress to the console for reference
  • Dispose of the site object to free up server resources

#Get site object and specify name of the library to look for in each site
$site = Get-SPSite http://portal
$lookForList = "Shared Documents"

#Walk through each site and change content types on the list specified
$site | Get-SPWeb -Limit all | ForEach-Object {
   
    write-host "Checking site:"$_.Title
   
    #Make sure content types are allowed on the list specified
    $docLibrary = $_.Lists[$lookForList]
   
    if ($docLibrary -ne $null)
    {
        $docLibrary.ContentTypesEnabled = $true
        $docLibrary.Update()
   
        #Add site content types to the list
        $ctToAdd = $site.RootWeb.ContentTypes["Sales Document"]
        $ct = $docLibrary.ContentTypes.Add($ctToAdd)
        write-host "Content type" $ct.Name "added to list" $docLibrary.Title
        $ctToAdd = $site.RootWeb.ContentTypes["IT Document"]
        $ct = $docLibrary.ContentTypes.Add($ctToAdd)
        write-host "Content type" $ct.Name "added to list" $docLibrary.Title
        $docLibrary.Update()
    }
    else
    {
        write-host "The list" $lookForList "does not exist in site" $_.Title
    }
}
#Dispose of the site object
$site.Dispose()

An example from the output of this script is shown below:

image

The second content type adding script performs the following tasks:

    • Sets up variables for connecting to the site collection and specifying the name of the content type to examine on all document libraries in each site 
    • Uses a piped command to walk through each site in the site collection and:
      • Get each document library in the site and check if a content type exists with the name specified for the $lookForCT variable (“Sales Document” in the example script below)
      • If the $lookForCT content type exists on the library, add the “HR Document” and “IT Document” content types to the document library
      • Output progress to the console for reference
    • Dispose of the site object to free up server resources

#Get site object and
#specify name of the content type to look for in each library
$site = Get-SPSite http://portal
$lookForCT = "Sales Document"

#Walk through each site in the site collection
$site | Get-SPWeb -Limit all | ForEach-Object {
   
    write-host "Checking site:"$_.Title
   
    #Go through each document library in the site
    $_.Lists | where { $_.BaseTemplate -eq "DocumentLibrary" } | ForEach-Object {
       
        write-host "Checking list:"$_.Title
       
        #Check to see if the library contains the content type specified
        #at the start of the script
        if (($_.ContentTypes | where { $_.Name -eq $lookForCT }) -eq $null)
        {
            write-host "No content type exists with the name" $lookForCT "on list" $_.Title
        }
        else
        {
            #Add site content types to the list
            $ctToAdd = $site.RootWeb.ContentTypes["HR Document"]
            $ct = $_.ContentTypes.Add($ctToAdd)
            write-host "Content type" $ct.Name "added to list" $_.Title
            $ctToAdd = $site.RootWeb.ContentTypes["IT Document"]
            $ct = $_.ContentTypes.Add($ctToAdd)
            write-host "Content type" $ct.Name "added to list" $_.Title
            $_.Update()
        }
    }
}
#Dispose of the site object
$site.Dispose()

An example of the output from the script is shown below:

image

Removing content types from lists

The first content type removal script performs these tasks:

  • Sets up variables for connecting to the site collection and specifying the name of the document library to examine in each site 
  • Uses a piped command to walk through each site in the site collection and:
    • Attach to the document library specified in the $lookForList variable (“Shared Documents” in the example script below)
    • Remove the “Sales Document” and “IT Document” content types from the document library
    • Output progress to the console for reference
  • Dispose of the site object to free up server resources

#Get site object and specify name of the library to look for in each site
$site = Get-SPSite http://portal
$lookForList = "Shared Documents”

#Walk through each site and change content types on the list specified
$site | Get-SPWeb -Limit all | ForEach-Object {
   
    write-host "Checking site:"$_.Title
   
    #Check list exists
    $docLibrary = $_.Lists[$lookForList]
   
    #Remove unwanted content types from the list
    if($docLibrary -ne $null)
    {
        $ctToRemove = $docLibrary.ContentTypes["IT Document"]
        write-host "Removing content type" $ctToRemove.Name "from list" $docLibrary.Title
        $docLibrary.ContentTypes.Delete($ctToRemove.Id)
        $docLibrary.Update()
    }
    else
    {
        write-host "The list" $lookForList "does not exist in site" $_.Title
    }
}
#Dispose of the site object
$site.Dispose()

An example of the output from the script is shown below:

image

Finally, the second content type removal script performs the following tasks:

    • Sets up variables for connecting to the site collection and specifying the name of the content type to examine on all document libraries in each site
    • Uses a piped command to walk through each site in the site collection and:
      • Get each document library in the site and check if a content type exists with the name specified for the $lookForCT variable (“Sales Document” in the example script below)
      • If the $lookForCT content type exists on the document library (“Sales Document” in the example script below), remove it
      • Output progress to the console for reference
    • Dispose of the site object to free up server resources

#Get site object and
#specify name of the content type to look for in each library
$site = Get-SPSite http://portal
$lookForCT = "Sales Document"

#Walk through each site in the site collection
$site | Get-SPWeb -Limit all | ForEach-Object {
   
    write-host "Checking site:"$_.Title
   
    #Go through each document library in the site
    $_.Lists | where { $_.BaseTemplate -eq "DocumentLibrary" } | ForEach-Object {
       
        write-host "Checking list:"$_.Title
       
        #Check to see if the library contains the content type specified
        #at the start of the script
        if (($_.ContentTypes | where { $_.Name -eq $lookForCT }) -eq $null)
        {
            write-host "No content type exists with the name" $lookForCT "on list" $_.Title
        }
        else
        {
            #Remove content types from list
            $ctToRemove = $_.ContentTypes[$lookForCT]
            write-host "Removing content type" $ctToRemove.Name "from list" $_.Title
            $_.ContentTypes.Delete($ctToRemove.Id)
            $_.Update()
        }
    }
}
#Dispose of the site object
$site.Dispose()

An example of the output from the script is shown below:

image

Note that a content type cannot be removed from a list if there are items currently using it in that list. Therefore, before you can attempt to remove a list content type, you must make sure that any items associated with it have either been assigned a different content type (see this article for details on how to do this) or deleted from the list completely.

Even after you think that all list items have been reassigned new content types or deleted, there still may be reasons you get the dreaded “Content type is still in use” message when trying to remove the content type. This is usually because there are still items present in the list that you are not able to see in standard list views. Here are a few tips to troubleshoot the message if you are still getting it:

  • Go to the document library showing the error, click Library Settings from the Library tab on the ribbon interface and then select Manage files which have no checked in version. You can use this administration page to take ownership of files created or uploaded by users but not checked in yet, and therefore invisible in list views. Once you have taken ownership, you can check them in and reassign the content types set on them
  • Check the recycle bin and delete files using the content type you wish to remove from the list
  • Remove any old versions of a document which may have used the content type you wish to remove from the list

Tuesday, 2 November 2010

Convert Word documents to PDF in SharePoint Server 2010 using PowerShell

SharePoint Server 2010 Standard and Enterprise editions includes a feature called Word Automation Services. There is an excellent article here explaining it in more detail, but in summary, the feature provides the capability for converting documents from one Microsoft Word format to another (e.g., .doc to .docx), converting Word documents to .pdf or .xps, and execute tasks during the opening of a document – such as updating the Table of Contents or index fields.

Whilst there are probably more applications for converting documents in workflows and event receivers, a batch of documents can also be converted using PowerShell. In this article I shall run through the basic steps involved in converting documents with an example of how to convert Word documents to PDF, plus some information on how you can speed up the conversion process and also remove the original Word documents from SharePoint, once successfully converted.

Before you can start converting documents, you will need to create a Word Automation Service Application instance from the Manage Service Applications page in Central Administration, and start the Word Automation Services service from the Manage services on server page, also in Central Administration. I’m also assuming that you have a site ready with a document library containing the Word documents to be converted, as shown below.

1

Once you have your service application running, you can type the following two lines in PowerShell to connect to the Word Automation Services Proxy and set up the variable for a new conversion job:

$wasp = Get-SPServiceApplicationProxy | where { $_.TypeName -eq "Word Automation Services Proxy" }
$job = New-Object Microsoft.Office.Word.Server.Conversions.ConversionJob($wasp)

Next, we will get the site, folder containing the Word documents to be converted, and folder to store the converted PDF documents (this folder can be the same). Note that in addition to converting documents in folders, the ConversionJob class also contains methods to add individual files or complete document libraries, if preferred:

$web = Get-SPWeb http://portal/team
$inputFolder = $web.GetFolder("Shared Documents/Word Documents")
$outputFolder = $web.GetFolder("Shared Documents/PDF Documents")

We can now set up our conversion job using the properties available in the ConversionJob class. This includes a description for the job, specifying PDF as the save format, and choosing to overwrite any PDF documents with the same name that currently exist in the folder:

$job.UserToken = $web.CurrentUser.UserToken
$job.Name = "Convert Hello Docs to PDF"
$job.Settings.OutputFormat = [Microsoft.Office.Word.Server.Conversions.SaveFormat]::PDF
$job.Settings.OutputSaveBehavior = [Microsoft.Office.Word.Server.Conversions.SaveBehavior]::AlwaysOverwrite

The AddFolder method below includes the input folder (source folder containing the Word documents), output folder (destination folder into which the converted PDF documents will be copied), and a boolean option specifying whether you want to include sub-folders under the input folder. For example, if this is set to $true, then documents will be converted from the input folder and all documents in sub-folders below it:

#Add input and output folders and start conversion
$job.AddFolder($inputFolder, $outputFolder, $false)
$job.Start()

You can view the status of your job by typing the following commands:

$status = New-Object Microsoft.Office.Word.Server.Conversions.ConversionJobStatus($wasp.Id, $job.JobId, $null)
$status

An example of a conversion status report is shown below:

2
If you are wondering why the status for your documents are shown as “NotStarted”, it is because they are converted by a timer job, which by default runs every 15 minutes. If you want to start this timer job now instead of waiting, type the following:

$watj = Get-SPTimerJob "Word Automation Service Application"
$watj.RunNow()

Once the timer job has been run, the updated status will look as follows:

3

There is a nice bit of C# code in this article from the Microsoft Word product team blog which waits until all documents have been converted and then deletes the successfully converted Word documents from the input location. I have adapted this for use in PowerShell below. Just tack it on to the end of the timer job commands if you want to include it in a PS1 script file:

[bool]$done = $false
write-host "Converting files - Please wait..."
while(!$done)
{
    Start-Sleep -s 5
    $status = New-Object Microsoft.Office.Word.Server.Conversions.ConversionJobStatus($wasp.Id, $job.JobId, $null)
   
    if ($status.Count -eq ($status.Succeeded + $status.Failed + $status.Canceled))
    {
        $done = $true
       
        #Delete original Word files successfully converted to PDF
        #Remove this code if you want to keep the documents in their original location
        $itemType = [Microsoft.Office.Word.Server.Conversions.ItemTypes]::Succeeded
        $items = $status.GetItems($itemType)
        foreach($item in $items) {
            $file = $web.GetFile($item.InputFile)
            $file.Delete()
        }
    }
}
write-host "Conversion operation complete - Status report:"
$status
$web.Dispose()

Once the job has completed, you should see the converted PDF files in the output location specified in the script:

4

Monday, 1 November 2010

Change the content type set on files in SharePoint using PowerShell

If you have to make changes to a site content type architecture, the requirement may exist to change a batch of files in a document library from one content type to another. This is usually because you either need to reclassify existing documents to a new content type or move documents to another content type so that unwanted content types can be removed from the list.

The process to change the content type on multiple files using the browser UI is both time consuming and very monotonous. Fortunately, PowerShell can do it for you in a matter of seconds. The script below sets up a function which will look at a document library and modify all files (including those in folders) associated with a specified content type to a different one. Before you can start changing files, run the script below:

function Reset-SPFileContentType ($WebUrl, $ListName, $OldCTName, $NewCTName)
{
    #Get web, list and content type objects
    $web = Get-SPWeb $WebUrl
    $list = $web.Lists[$ListName]
    $oldCT = $list.ContentTypes[$OldCTName]
    $newCT = $list.ContentTypes[$NewCTName]
    $newCTID = $newCT.ID
   
    #Check if the values specified for the content types actually exist on the list
    if (($oldCT -ne $null) -and ($newCT -ne $null))
    {
        #Go through each item in the list
        $list.Items | ForEach-Object {
            #Check if the item content type currently equals the old content type specified
            if ($_.ContentType.Name -eq $oldCT.Name)
            {
                #Check the check out status of the file
                if ($_.File.CheckOutType -eq "None")
                {
                    #Change the content type association for the item
                    $_.File.CheckOut()
                    write-host "Resetting content type for file" $_.Name "from" $oldCT.Name "to" $newCT.Name
                    $_["ContentTypeId"] = $newCTID
                    $_.Update()
                    $_.File.CheckIn("Content type changed to " + $newCT.Name, 1)
                }
                else
                {
                    write-host "File" $_.Name "is checked out to" $_.File.CheckedOutByUser.ToString() "and cannot be modified"
                }
            }
            else
            {
                write-host "File" $_.Name "is associated with the content type" $_.ContentType.Name "and shall not be modified"
            }
        }
    }
    else
    {
        write-host "One of the content types specified has not been attached to the list"$list.Title
    }
    $web.Dispose()
}

Once the script has been run, you can use it by typing the following command:

Reset-SPFileContentType –WebUrl <Site URL> –ListName <Document library display name> –OldCTName <Content type to be replaced> –NewCTName <Content type to replace it with>

For example, the following command will step through all files in a document library called “Shared Documents” from the site http://portal/team, and change all documents associated with the “Document” content type to one called “Sales Document”:

Reset-SPFileContentType –WebUrl “http://portal/team” –ListName “Shared Documents” –OldCTName “Document” –NewCTName “Sales Document”

Note that the new content type must be associated with the list before you run the command – the script will not attach it for you (I’ll be providing scripts for attaching and removing content types on lists in future posts).

A potential issue with performing this sort of change is that you may lose some column values during the content type move – even if the same columns exist on both the old and new content type. I did some simple testing with the standard “Title” column, a custom text column, and a choice column and values were successfully retained when I changed the content type – even on non-Microsoft Office documents. However, as I cannot guarantee this will be the case with all columns or column types, I strongly recommend that you attempt the move in a test document library or development environment prior to running the script for real.

image