Wednesday, 1 December 2010

Upload multiple user profile pictures/photos to SharePoint using PowerShell

This is one of those scripts that has been a while in the making, and it was only when a colleague of mine pointed something out today (thanks Rob!) that I realised where I was going wrong – more on that later.

In summary, the script performs the following tasks:

  • Uploads multiple image files from a local or network folder into the “User Photos” document library of the My Site host site. You could leave this part of the script out and upload them directly to this library using the SharePoint UI if you wanted to.
  • Configures the user profile for users associated with each image file. The profile is initially set to reference the image file uploaded to the “User Photos” document library.
  • Runs the Update-SPProfilePhotoStore cmdlet included in SharePoint 2010. This will create the three thumbnail images in the “Profile Pictures” folder of the “User Photos” document library and change each user profile to reference the medium sized thumbnail image. I have decided to leave the original image file in the root of the “User Photos” library for central storage and management.

There are a couple of pre-requisites to note here:

  • The image files must be in the format domainname_username.extension for the script to work properly. For example, a .jpg file for the user Phil.Childs in domain TESTDOMAIN will have to be named testdomain_phil.childs.jpg. This is important, as the script will use the file name to establish which user the image file relates to and update the user profile accordingly.
  • Ensure you have full permissions to manage the user profile service application. The user running the script (which I assume is some sort of SharePoint Administrator) must have been granted the Manage Profiles right in the Administrators section and Full Control rights in the Permissions section of the User Profile Service Application (pictured below).

image

Before running the script, I created a C:\Install\Photos folder containing my images on the local hard drive of the SharePoint server:

image

I then run the PowerShell script below – Note that running this script this will just set up the function in PowerShell and will not actually do anything until I call it with a command later on:

function Upload-PhotosToSP
{
    Param (
           [parameter(Mandatory=$true)][string]$LocalPath,
           [parameter(Mandatory=$true)][string]$MySiteUrl,
           [parameter(Mandatory=$false)][switch]$Overwrite
           )
   
    #Get site, web and profile manager objects
    $mySiteHostSite = Get-SPSite $MySiteUrl
    $mySiteHostWeb = $mySiteHostSite.OpenWeb()
    $context = Get-SPServiceContext $mySiteHostSite
    $profileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context)
    try
    {  
        #Get files from local folder
        $localPhotosFolder = Get-ChildItem $LocalPath
        #Get User Photos document library in the My Site host site
        $spPhotosFolder = $mySiteHostWeb.GetFolder("User Photos")
       
        #Upload each image file and configure user profiles
        $localPhotosFolder | ForEach-Object {
       
            #Generate file path for upload into SharePoint
            $spFullPath = $spPhotosFolder.Url + "/" + $_.Name
               
            #Check if the file exists and the overwrite option is selected before adding the file
            if ((!$mySiteHostWeb.GetFile($spFullPath).Exists) -or ($Overwrite)) {
                #Add file to the User Photos library
                write-host "Copying" $_.Name "to" $spFullPath.Replace("/" + $_.Name,"") "in" $mySiteHostWeb.Title "..." -foregroundcolor Green
                $spFile = $spPhotosFolder.Files.Add($spFullPath, $_.OpenRead(), $true)
                $spImagePath = $mySiteHostWeb.Url + "/" + $spFile.Url
               
                #Get the domain and user name from the image file name
                $domainName = $_.Name.Split("_")[0]
                $userName = $_.Name.Split("_")[1].Replace($_.Extension, "")
                $adAccount = $domainName + "\" + $userName
               
                #Check to see if user profile exists
                if ($profileManager.UserExists($adAccount))
                {
                    #Get user profile and change the Picture URL value
                    $up = $profileManager.GetUserProfile($adAccount)
                    $up["PictureURL"].Value = $spImagePath
                    $up.Commit()
                }
                else
                {
                    write-host "Profile for user"$adAccount "cannot be found"
                }
            }
            else
            {
                write-host "`nFile"$_.Name "already exists in" $spFullPath.Replace("/" + $_.Name,"") "and shall not be uploaded" -foregroundcolor Red
            }
        }
       
        #Run the Update-SPProfilePhotoStore cmdlet to create image thumbnails and update user profiles
        write-host "Waiting to update profile photo store - Please wait..."
        Start-Sleep -s 60
        Update-SPProfilePhotoStore –MySiteHostLocation $MySiteUrl
        write-host "Profile photo store update run - please check thumbnails are present in Profile Pictures folder."
    }
    catch
    {
        write-host "The script has stopped because there has been an error: "$_
    }
    finally
    {
        #Dispose of site and web objects
        $mySiteHostWeb.Dispose()
        $mySiteHostSite.Dispose()
    }
}

At this point you will need to know the URL of your My Site host site – this is typically the root site collection of a dedicated Web Application (e.g., http://MySite), but could differ depending on your environment. On my development environment it is http://portal/personal/mysite, and so I can call the function above with the following command:

Upload-PhotosToSP -LocalPath "C:\Install\Photos" -MySiteUrl "http://portal/personal/MySite" -Overwrite

When you run the function, you will get an output to the console similar to the one shown below:

image

The script will copy the image files to the root of the “User Photos” document library…

image

…and providing enough time had passed before the Update-SPProfilePhotoStore cmdlet ran, thumbnails will be present in the “Profile Pictures” folder:

image

If you need to update the image for a user, copy the file into the local or network folder you used before and just run the script again with the Overwrite switch specified.

Now to where I was going wrong. As mentioned earlier, I run the Update-SPProfilePhotoStore cmdlet at the end of the script to create the required image thumbnails and re-configure user profiles to reference the thumbnail instead of the originally uploaded image. When I originally ran this cmdlet to test the script, nothing happened – no errors and no changes to the images. However, and I’m not sure why (can anyone enlighten me?), but I have found that you need to wait a while after initially configuring the user profiles before running the cmdlet.

Therefore, in the script, I have added a sleep command of 60 seconds before running Update-SPProfilePhotoStore, which worked on my development machine with only a few images. This may or may not be enough in your environment, and so you will need to check that the thumbnail images have been successfully created in the “Profile Pictures” folder of the “User Photos” document library to ensure the script has run successfully. If not, you can always increase the amount of time to wait in the script by altering the number in the Start-Sleep -s 60 line, or run the cmdlet manually with the following syntax:

Update-SPProfilePhotoStore –MySiteHostLocation <MySiteHostURL>

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

Wednesday, 27 October 2010

Can’t remove a site column from a content type or list in SharePoint? Use PowerShell

There are particular site columns in SharePoint, which once added to a site content type or list cannot be removed again using the browser UI. An example of one of these columns in SharePoint Server 2010 is “Aliases”. If I add it to a content type and then click on the column name to remove it, you can see that the Remove button is not available.

image

This can cause much frustration and may even prompt you to completely delete the content type and start again – something that is usually impossible to do on a live environment.

Fortunately, we have PowerShell available to us for solving such problems. The script below will attach to the site containing your content type (also works for content type hubs), the content type itself, and then the link to the column within the content type. It then only takes a couple of lines to remove the column and update the content type.

The example below will remove the “Aliases” site column from the “Sales Document” site content type in http://portal:

#Attach to the web and content type
$web = Get-SPWeb
http://portal
$ct = $web.ContentTypes["Sales Document"]

#Get link to the columnn from the web
$spFieldLink = New-Object Microsoft.SharePoint.SPFieldLink ($web.Fields["Aliases"])

#Remove the column from the content type and update
$ct.FieldLinks.Delete($spFieldLink.Id)
$ct.Update()

#Dispose of the web object
$web.Dispose()

Phew!

Note that any columns of this type deleted from a parent content type will not automatically disappear in the child content types below it. The easiest way to get around this is to use the same script above to remove the site column from child content types, too.

Unfortunately, you may also find that if the content types containing this site column were attached to lists, then the lists themselves will also have the column added to them – even after removing the column from all associated content types. If this is the case, you will not be able to remove the column in the list using the browser UI as the Remove button will be missing on the column administration page here also.

To resolve this, you can either decide to remove the column using PowerShell one list at a time, or if there is a way of defining a batch of lists, use a script to modify multiple lists in one go. The script below provides you with an example of how to walk through each document library of a specified name on each site of the site collection and delete the offending column. If you do decide to run a script like this, please ensure you have fully tested it in a development environment beforehand due to the potential damage that it could inflict:

#Delete column on a specified list in all sites of a site collection
$site = Get-SPSite http://portal
$site | Get-SPWeb -Limit all | ForEach-Object {
    #Specify list which contains the column
    $list = $_.Lists["Pages"]
    #Specify column to be deleted
    $field = $list.Fields["Aliases"]
    #Allow column to be deleted
    $field.AllowDeletion = $true
    #Delete the column
    $field.Delete()
    #Update the list
    $list.Update()
}
$site.Dispose()

Friday, 22 October 2010

Create SharePoint search keywords and best bets from a CSV file using PowerShell

If your SharePoint implementation comprises of different Search Centers across a number of site collections, managing a consistent set of keywords and best bets can be difficult through the browser UI. Each keyword can be time consuming to create, especially if it contains multiple best bets.

The script in this article allows you to use a CSV file to specify a set of keywords and best bets for a specified site collection. You can either append to any existing keywords already created in the site collection, or wipe out the current keywords and create new ones.

First, the CSV file should have the following header:

Keyword,Definition,Synonyms,BestBet1,Description1,Url1,BestBet2,Description2,Url2,BestBet3,Description3,Url3

I have specified three best bets in the example above, but you are free to add as many BestBet, Description, and Url columns as you like. Although hard to read on this page, here is an example of a CSV file with some example keywords included:

Keyword,Definition,Synonyms,BestBet1,Description1,Url1,BestBet2,Description2,Url2,BestBet3,Description3,Url3
PowerShell,Here are some links for the keyword 'PowerShell',Windows PowerShell;Scripting;PS;Scripts,Get-SPScripts,PowerShell scripts for SharePoint,
http://get-spscripts.com,Hey Scripting Guy! Blog,Microsoft TechNet blog on scripting,http://blogs.technet.com/b/heyscriptingguy/default.aspx,Effective Windows PowerShell,Free e-Book on learning PowerShell,http://keithhill.spaces.live.com/blog/cns!5A8D2641E0963A97!6930.entry
SharePoint,Here are some links for the keyword 'SharePoint',SharePoint Server;SP2010;Foundation;Collaboration,PACSharePoint.com,SharePoint blog offering hints tips and solutions,http://pacsharepoint.com,Microsoft SharePoint Team Blog,The official blog of the Microsoft SharePoint Product Group,http://blogs.msdn.com/b/sharepoint/,,,

This CSV file will add two keywords – “PowerShell” and “SharePoint” – along with synonyms and best bets for each one. Note that where there is more than one synonym for each keyword, add them as semi-colon delimited values within the CSV column.

Now the script. Before you can add the keywords, you must run the script below in PowerShell, which will set up the Import-SearchKeywords function. You will need to replace “Search Service Application” with the name of your search service application proxy from the Manage Service Applications page in Central Administration:

function Import-SearchKeywords ($SiteUrl, $CSVFilePath, [switch]$RemoveOldKeywords)
{
    $ssap = Get-SPEnterpriseSearchServiceApplicationProxy -Identity "Search Service Application"
    $keywords = New-Object Microsoft.Office.Server.Search.Administration.Keywords($ssap, $SiteUrl)
    $allKeywords = $keywords.AllKeywords
    $date = Get-Date
   
    #Remove all previous keywords from the site collection if chosen
    if($RemoveOldKeywords) {
        $keywordsArray = @()
        $allKeywords | ForEach-Object {
            $keywordsArray = $keywordsArray + $_.Term
        }
        $keywordsArray | ForEach-Object {
            write-host "Deleting keyword:"$_
            $allKeywords[$_].Delete()
        }
    }
   
    #Import CSV file
    $csvData = Import-Csv $CSVFilePath | ForEach-Object {
        #Create keyword
        write-host "Importing keyword:"$_.Keyword
        $keyword = $allKeywords.Create($_.Keyword, $date.AddHours(-1))
        $keyword.Definition = $_.Definition
       
        #Create synonyms
        $synonymArray = @()
        $synonymArray = $_.Synonyms.Split(";")
        $synonymArray | ForEach-Object {
            write-host "Creating synonym"$_ "for keyword" $keyword.Term
            if (($_ -ne $null) -and ($_ -ne "")) { $keyword.Synonyms.Create($_) }
        }
       
        #Create best bets
        $bestBetColumnNumber = 1
        $bestBetColumn = "BestBet" + $bestBetColumnNumber
        $descriptionColumn = "Description" + $bestBetColumnNumber
        $urlColumn = "Url" + $bestBetColumnNumber
       
        while ($_.$bestBetColumn) {
            write-host "Creating best bet"$_.$bestBetColumn "for keyword" $keyword.Term
            $keyword.BestBets.Create($_.$bestBetColumn, $_.$descriptionColumn, $_.$urlColumn)
           
            $bestBetColumnNumber = $bestBetColumnNumber + 1
            $bestBetColumn = "BestBet" + $bestBetColumnNumber
            $descriptionColumn = "Description" + $bestBetColumnNumber
            $urlColumn = "Url" + $bestBetColumnNumber
        }
       
        #Update keyword with best bets
        $keyword.Update()
    }
}

Once you have your CSV file ready and the script above has been run, you have one of two options for importing the keywords and best bets.

To append the keywords in your CSV with those already present in the site collection, type the following command:

Import-SearchKeywords –SiteUrl <SiteCollectionURL> –CSVFilePath <CSVFilePathAndFileName>

To delete all existing keywords from the site collection and create a new set from scratch with the contents of your CSV file, type the same command but with the RemoveOldKeywords switch:

Import-SearchKeywords –SiteUrl <SiteCollectionURL> –CSVFilePath <CSVFilePathAndFileName> -RemoveOldKeywords

For example, to create an entirely new set of keywords in the “http://portal” site collection using my example “SearchKeywords.csv” file above, you would type:

Import-SearchKeywords -SiteUrl http://portal -CSVFilePath C:\Install\SearchKeywords.csv –RemoveOldKeywords

The “Manage Keywords” site collection administration page will now look as follows:

image

And the following results will appear when I type one of my keywords in the search center:

image

If you are looking to import FAST Search keywords and user contexts to SharePoint 2010 from a CSV file, take a look at this script posted on the Microsoft TechNet Script Center at http://gallery.technet.microsoft.com/ScriptCenter/en-us/9ad93d90-9ca7-4515-b81e-441eda0390c3.

Thursday, 21 October 2010

Change SharePoint list settings using PowerShell - Part 2: Advanced Settings

This article follows on from Part 1 last week, where I detailed how to change the General and Versioning Settings of a SharePoint list or document library with PowerShell.

This part covers changing the Advanced Settings of a list. As a recap, you can use the following script to change the settings of a specifically named list on each site in the site collection. In the example below, we are changing the settings of a document library called “Shared Documents” on each site in the “http://portal” site collection:

#Change these variables to your site URL and list name
$site = Get-SPSite http://portal
$listName = "Shared Documents"

#Walk through each site in the site collection
$site | Get-SPWeb | ForEach-Object {

#Get the list in this site
$list = $_.Lists[$listName]

#Make the list changes

#Update the list
$list.Update()
}

#Dispose of the site object
$site.Dispose()

The table below shows the Advanced Settings we can change, with snapshots of the same settings from the list admin UI. To use any of these settings from the table, copy them into the script above, underneath the “#Make the list changes” line:

Document Template
(document libraries only)
image
The following set of commands will upload a new document template from a path on your computer or network (UNC) path and change the document template setting on the list:

#Upload new document template file from computer
$newTemplateFile = Get-ChildItem "C:\LocalPath\Test Document.dotx"
$templateFolderPath = $_.Url + "/" + $list.RootFolder.Url + "/Forms"
$templateFolder = $_.GetFolder($templateFolderPath)
$uploadPath = $templateFolder.Url + "/" + $newTemplateFile.Name
$spNewTemplateFile = $templateFolder.Files.Add($uploadPath, $newTemplateFile.OpenRead(), $true)

#Change list setting to the template file uploaded
$list.DocumentTemplateUrl = $uploadPath

Note: If you are running this script on a single site rather than in bulk on each site in the site collection, change the $_ references above to the variable you have assigned to the site – e.g., $web

Opening Documents in the Browser
(document libraries only)
image
There are a couple of settings affecting these options. Use the following commands to set one of the first two options:

#Open in the client application
$list.DefaultItemOpen = "PreferClient”

#Open in the browser
$list.DefaultItemOpen = "Browser"

If you want to set the “Use the server default” option, use the following command:

$list.DefaultItemOpenUseListSetting = $false
Custom Send To Destination
(document libraries only)
image
For this option you have to set a destination name and URL of the custom Send To location:

$list.SendToLocationName = "Home Site"
$list.SendToLocationUrl = "http://Portal/Shared Documents"

Item-level Permissions
(lists only)
There are two options on this section, depending on whether you are looking to restrict user read or write access.

Read access
This setting uses a numerical value to determine which option is set:

Read all items = 1
Read items that were created by the user = 2

image
For example, the following command would be used to configure this setting to “Read items that were created by the user”, as pictured above:

$list.ReadSecurity = 2

Create and Edit access
This setting also uses a numerical value to set the option desired:
Create and edit all items = 1
Create items and edit items that were created by the user = 2
None = 4

image
For example, the following command would be used to configure this setting to “None”, as pictured above:

$list.WriteSecurity = 4
E-Mail Notification
(task and issue based lists only)
image
This command will enable the “Send e-mail when ownership is assigned” option:

$list.EnableAssignToEmail = $true
Attachments
(lists only)
image
The following command will disable attachments on the list:

$list.EnableAttachments = $false
Folders image
Use the following line to disable the “New Folder” command on list and library menus:

$list.EnableFolderCreation = $false
Search image
Careful with the wording on this one, as setting it to $true will set it to No in the UI, as pictured above:

$list.NoCrawl = $true
Offline Client Availability image
Similar to the way the Search option works above, setting this one to $true will set it to No in the UI, as pictured above:

$list.ExcludeFromOfflineClient = $true
Site Assets Library
(document libraries only)
image
This setting will switch the document library to be a site assets library:

$list.IsSiteAssetsLibrary = $true
Datasheet image
Careful with the wording on this one also, as setting it to $false will set it to Yes in the UI, as pictured above:

$list.DisableGridEditing = $false
Dialogs image
Similar to the way the Datasheet option works above, setting this one to $true will set it to No in the UI, as pictured above:

$list.NavigateForFormsPages = $true

In Part 3 of this series, I’ll cover a few extra settings you can configure on a list with PowerShell, including some options hidden from the UI…

Thursday, 14 October 2010

Create multiple SharePoint sites with permissions using a CSV file and PowerShell

When defining hierarchical structures, I personally prefer using an XML file, but there is still a place for the good old Comma-separated values (CSV) format. Firstly, it is easier to create and modify a CSV file – especially when using an application like Excel – and as the format has been around for a while, most people find it easier to read and understand. Despite its flaws, this must be the major reason why Microsoft decided to use CSV as the format for the Managed Metadata import file in SharePoint 2010.

Now we have the New-SPWeb PowerShell cmdlet introduced in SharePoint 2010, creating individual sites and sub-sites is easy enough to do without needing to visit the browser UI, but you still need to put in a bit of work if you are looking to create an entire site hierarchy using PowerShell.

To help you out, I have uploaded a “Create SharePoint Sites from CSV” PS1 script file to Codeplex for download. This script can be used to create multiple SharePoint sites in bulk from a CSV file and also set permissions on each site during creation.

It includes the following features:

  • Choose any site from which to start creating sub-sites
  • Create a full site hierarchy with sub-sites down to three levels from the start site
  • Specify a site title, description, and URL name
  • Specify whether to inherit parent site top navigation bar
  • Specify whether to inherit parent site permissions
  • If breaking permission inheritance, specify whether to add new permissions from scratch or by copying over permissions from the parent site and adding to those
  • Assign site permissions to SharePoint groups, Active Directory users or Active Directory groups, specifying the permission level required
  • Create the specified SharePoint group or Active Directory account in the site, if they do not already exist

CreateSitesFromCSV_Output

You can download the script, documentation and an example CSV file at http://spcreatesitesfromcsv.codeplex.com. As always, I welcome any comments, suggestions, ideas or contributions for any new features if you have them.

Wednesday, 13 October 2010

Delete multiple SharePoint sites using PowerShell

It can be very time consuming in the SharePoint browser UI to delete a large structure of sub-sites below a particular site. This is because you are unable to delete a site that contains other sub-sites without deleting those sub-sites first, which in turn may contain other sub-sites!

The PowerShell script below allows you to choose a start site (i.e., the site from which you want to delete all sub-sites), walks through each sub-site and deletes it, starting from sites at the end of the hierarchy and working backwards until it returns back to the start site, which it will not delete.

First, run the PowerShell script below – Note: Doing this will not start deleting sites as you have to call the functions in the script with a separate command (see below).

function Delete-SPSubSites ($StartSite)
{
    Start-SPAssignment –Global
    #Get the starting site from which to delete all sub-sites
    $web = Get-SPWeb $StartSite
    #Run function to get all sub-sites below the start site
    Get-SPSubWebs -StartWeb $web
    Stop-SPAssignment –Global
}

function Get-SPSubWebs ($StartWeb)
{
    #Get all sub-sites below the start site
    $webs = $StartWeb.GetSubwebsForCurrentUser()
    #Run this routine if there are sub-sites present
    if ($webs.Count -gt 0)
    {
        #Walk through each sub-site
        foreach ($nextWeb in $webs)
        {
            #Check for sub-sites underneath this site
            Get-SPSubWebs -StartWeb $nextWeb
            #Run function to delete the site
            Delete-SPSubWeb -DeleteWeb $nextWeb
            $nextWeb.Dispose()
        }
    }
}

function Delete-SPSubWeb ($DeleteWeb)
{
    #Delete the site specified in the $DeleteWeb variable
    write-host "Deleting site"$DeleteWeb.Title"at"$DeleteWeb.Url
    $DeleteWeb.Delete()
    $DeleteWeb.Dispose()
}

Once you have run the script, you can use it to delete sub-sites by typing the following command in the PowerShell console:

Delete-SPSubSites -StartSite http://portal/team

The example above will delete all sub-sites below http://portal/team, but will not delete the http://portal/team site itself.

image

In case it’s not obvious (!), be very careful with the use of this script. Take a backup of your site collection first, and run on a development server before using this script in a live environment to ensure you delete the correct sites.

Tuesday, 12 October 2010

Change SharePoint list settings using PowerShell - Part 1: General and Versioning

Let’s say that you have 100 team sites created and being used with a document library called Shared Documents available in each site. You are then asked to change the versioning settings on each Shared Documents library in every one of those 100 sites to create major and minor versions. To resolve this requirement for any new document libraries and sites created in the future, you can wrap up the changes into list and site templates. However, doing this will not affect any of the 100 existing document libraries already being used.

Enter PowerShell. In part one of this series, I will show you how you can change the General and Versioning Settings of a list. First, we’ll change the settings of a single list in a single site. Whilst this is easy enough to do in the UI and unlikely to be used in a script, it will give you an idea of how the script works:

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

#Make the list changes
$list.Title = "Documents"

#Update the list and dispose of the web object
$list.Update()
$web.Dispose()

The script above gets a document library called “Shared Documents” in the site “http://portal/team”, sets a new title for the document library to “Documents”, and updates the settings for the list. If there is more than one setting to change, we could have any number of settings defined under the “#Make the list changes” line and they will all be updated by the $list.Update() command.

The script below would certainly take a lot longer to do in the UI, as it walks through each site in the “http://portal” site collection, changing the title and description of the document library called “Shared Documents” in each site – all you have to do is tell the script the relevant site collection URL, list name, and which settings you want to change on each list with the name specified:

#Change these variables to your site URL and list name
$site = Get-SPSite http://portal
$listName = "Shared Documents"

#Walk through each site in the site collection
$site | Get-SPWeb | ForEach-Object {

#Get the list in this site
$list = $_.Lists[$listName]

#Make the list changes
$list.Title = "Documents"
$list.Description = "Use the Documents library to store shared files"

#Update the list
$list.Update()
}

#Dispose of the site object
$site.Dispose()

The table below shows the other General and Versioning Settings we can also change, with snapshots of the same settings from the list admin UI. To use any of these settings from the table, copy them into the script above, underneath the “#Make the list changes” line.

General Settings

List Name image
The following command will change the list display name (note, this will not change the list URL):

$list.Title = "Documents"
List Description image
The following command will change the list description:

$list.Description = "Use the Documents library to store shared files"
Navigation image
To configure the list so that it shows on the Quick Launch (left-hand site navigation), use this command:

$list.OnQuickLaunch = $true

Change the $true to $false if you want to remove the list from being shown on the Quick Launch.

Versioning Settings

Content Approval image
This command will change the list to require approval for all items:

$list.EnableModeration = $true

Change the $true to $false if you want to set content approval to No.
Document Version History
(settings and wording can be different between document libraries and lists, as annotated in the script code)
image
There are a few settings on this section, which I have annotated in the commands below:

#Create a version each time you edit an item in this list (lists)
#Create major versions (document libraries)
$list.EnableVersioning = $true

#Create major and minor (draft) versions (document libraries only)
$list.EnableMinorVersions = $true

#Keep the following number of versions (lists)
#Keep the following number of major versions (document libraries)
$list.MajorVersionLimit = 5

#Keep drafts for the following number of approved versions (lists)
#Keep drafts for the following number of major versions (document libraries)
$list.MajorWithMinorVersionsLimit = 5
Draft Item Security image
This setting uses a numerical value to determine which option is set:
Any user who can read items = 0
Only users who can edit items = 1
Only users who can approve items (and the author of the item) = 2

For example, to configure this setting for “Only users who can edit items”, as pictured above, use this command:

$list.DraftVersionVisibility = 1
Require Check Out
(document libraries only)
image
This command will change the list to require documents to be checked out before they can be edited:

$list.ForceCheckout = $true

In Part 2 of this series, I cover how to use PowerShell for configuring the Advanced Settings of a list.