SharePoint 2010, Powershell - Loop through all Document Libraries, create view and set it as default - sharepoint

I'm having a little trouble with a script I'm working on for SharePoint 2010. I'm only a beginner with Powershell so haven't been able to to spot the no doubt obvious and glaring problem.
The desire for the script is to loop through each web-site in each site collection, create a view for Document Libraries only and set that view as default.
Issue 1) Currently it appears to enumerate the Document Libraries but then creates the view multiple times in the first library it found. Something is wrong with the foreach loops but I don't know what.
Issue 2) I need to integrate this section so I can set the view as default but I'm not too sure where to stick it so it loops through with the changes to each library.
$site= New-Object Microsoft.SharePoint.SPSite $siteURL
$web=$site.OpenWeb()
$list=$web.Lists["$list"]
$view=$list.Views["Detailed"]
$view.DefaultView = $true
$view.Update()
$list.Update()
Any assistance on these two issues would be of great help:-). Thanks, Ashley
Full Script:
Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue
$siteURL = "http://sp14fdev01/"
$site = Get-SPSite($siteURL)
foreach($web in $site.AllWebs) {
foreach($list in $web.Lists) {
if($list.BaseType -eq "DocumentLibrary") {
$site = New-Object Microsoft.SharePoint.SPSite($SiteURL) ;
$web = $site.OpenWeb($SiteURL);
$list = $web.Lists.item($listname);
$viewfields = New-Object System.Collections.Specialized.StringCollection
$viewfields.Add("DocIcon")
$viewfields.Add("LinkFilename")
$viewfields.Add("_UIVersionString")
$viewfields.Add("FileSizeDisplay")
$viewfields.Add("Created")
$viewfields.Add("Modified")
$viewfields.Add("Editor")
[void]$list.Views.Add("Detailed", $viewfields, "", 100, $true, $true)
$list.Update();
}}
$web.Dispose();
$site.Dispose();
}

The changes are explained in the comments.
Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue
$siteURL = "http://sp14fdev01/"
$site = Get-SPSite($siteURL)
foreach($web in $site.AllWebs) {
foreach($list in $web.Lists) {
if($list.BaseType -eq "DocumentLibrary") {
// the variables `$web` and `$list` already reference the objects you need
//$site = New-Object Microsoft.SharePoint.SPSite($SiteURL) ;
//$web = $site.OpenWeb($SiteURL);
// new instance of the list is necessary to avoid the error "Collection was modified"
$newList = $web.Lists.item($list.ID);
$viewfields = New-Object System.Collections.Specialized.StringCollection
$viewfields.Add("DocIcon")
$viewfields.Add("LinkFilename")
$viewfields.Add("_UIVersionString")
$viewfields.Add("FileSizeDisplay")
$viewfields.Add("Created")
$viewfields.Add("Modified")
$viewfields.Add("Editor")
[void]$newList.Views.Add("Detailed", $viewfields, "", 100, $true, $true)
$newList.Update();
// setting the default view
$view=$newList.Views["Detailed"]
$view.DefaultView = $true
$view.Update()
}
}
$web.Dispose();
}
$site.Dispose();

Related

Running a powershell script using multiple threads

I'm trying to execute a powershell script from within another powershell script. My current script runs fine, but I'd like to try and speed it up if possible.
What my script does is import a list of contacts into Each users Contacts folder through EWS.
In my powershell script the script that handles the importing I call it like this (ImportContacts is a Function without any arguments):
. $rootPath\ImportContacts\ImportContacts.ps1
ImportContacts
When I run it normally, as I mentioned above everything works fine, I just would like to speed it up. I tried following some examples of implementing runspacepool in Powershell to take advantage of using multiple threads, but it doesn't seem to be working properly for me. I'm sure it's a silly syntax error, but I have this currently:
Measure-Command{
$MaxThreads = 5
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads)
$RunspacePool.Open()
$Jobs = #()
ImportContacts | Foreach-Object {
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
$PowerShell.AddScript({ImportContacts})
$Jobs += $PowerShell.BeginInvoke()
}
while ($Jobs.IsCompleted -contains $false)
{
Start-Sleep 1
}}
This seems to do the job, but I can't tell a difference with the speed.
EDIT 5-15-21
To better assist with the question, here is how I retrieve the user data. I have a file I call called "ExportedContacts.ps1"
It's called like so:
$Users = & $rootPath\ExportContacts\ExportContacts.ps1
The contents of the file is this
$Users = Get-ADUser -Filter * -Properties extensionAttribute2, middlename, mobile, OfficePhone, GivenName, Surname, DisplayName, EmailAddress, Title, Company, Department, thumbnailPhoto | Where-Object {($_.extensionAttribute2 -like "DynamicDistro") -AND (($_.Mobile -ne $NULL) -OR ($_.OfficePhone -ne $NULL))} | Select-Object #{Name="First Name";Expression={$_.GivenName}},#{Name="Last Name";Expression={$_.Surname}},#{Name="Display Name";Expression={$_.DisplayName}},#{Name="Job Title";Expression={$_.Title}},#{Name="Company";Expression={$_.Company}},#{Name="Department";Expression={$_.Department}},#{Name="Mobile Phone";Expression={$_.Mobile}},#{Name="Home Phone";Expression={$_.OfficePhone}}, #{Name="Middle Name";Expression={$_.MiddleName}}, #{Name="E-mail Address";Expression={$_.EmailAddress}}, thumbnailPhoto | Sort-Object "Last Name"
return $Users
Then I import those contacts, similarly to how I mentioned above. The content of the import is as follows:
Function ImportContacts
{
Write-Host "Importing Contacts. This can take several minutes."
#******************************************************************
foreach ($ContactItem in $Users)
{
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
$ExchangeContact = New-Object Microsoft.Exchange.WebServices.Data.Contact($service);
$ExchangeContact.NickName = ('{0} {1}' -f $ContactItem."First Name", $ContactItem."Last Name"). Trim()
$ExchangeContact.DisplayName = $ExchangeContact.NickName;
$ExchangeContact.FileAs = $ExchangeContact.NickName;
$ExchangeContact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1] = $ContactItem."E-mail Address";
$ExchangeContact.PhoneNumbers[[Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::HomePhone] = $ContactItem."Home Phone";
$ExchangeContact.PhoneNumbers[[Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::MobilePhone] = $ContactItem."Mobile Phone";
$ExchangeContact.Department = $ContactItem."Department";
$ExchangeContact.CompanyName = $ContactItem."Company";
$ExchangeContact.JobTitle = $ContactItem."Job Title";
$ExchangeContact.MiddleName = $ContactItem."Middle Name";
# Save the contact
$ExchangeContact.Save($ContactsFolder.Id);
}
}
I am also including the File that creates the contacts folder I specify and also deletes existing contacts (if the folder already exists), so that each import is a clean and updated import. I'd be curious if there is a faster way to clean the existing items?
Function CreateContactsFolder
{
Write-Host "Creating Contacts Folder and Cleaning up stale items. This can take a couple minutes."
Try
{
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
$RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
$RootFolder.Load()
#Check to see if they have a contacts folder that we want
$FolderView = new-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
$ContactsFolderSearch = $RootFolder.FindFolders($FolderView) | Where-Object {$_.DisplayName -eq $FolderName}
if($ContactsFolderSearch)
{
$ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind($service,$ContactsFolderSearch.Id);
#If folder exists, connect to it. Clear existing Contacts, and reupload new (UPDATED) Contact Info
Write-Host "Folder alreads exists. We will remove all contacts under this folder."
# Attempt to empty the target folder up to 10 times.
$tries = 0
$max_tries = 0
while ($tries -lt 2)
{
try
{
$tries++
$ErrorActionPreference='Stop'
$ContactsFolder.Empty([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete, $true)
$tries++
}
catch
{
$ErrorActionPreference='SilentlyContinue'
$rnd = Get-Random -Minimum 1 -Maximum 10
Start-Sleep -Seconds $rnd
$tries = $tries - 1
$max_tries++
if ($max_tries -gt 100)
{
Write-Host "Error; Cannot empty the target folder; `t$EmailAddress"
}
}
}
}
else
{
#Contact Folder doesn't exist. Let's create it
try
{
Write-Host "Creating new Contacts Folder called $FolderName"
$ContactsFolder = New-Object Microsoft.Exchange.WebServices.Data.ContactsFolder($service);
$ContactsFolder.DisplayName = $FolderName
$ContactsFolder.Save([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
}
catch
{
Write-Host "Error; Cannot create the target folder; `t$EmailAddress"
}
}
return $ContactsFolder
}
Catch
{
Write-Host "Couldn't connect to the user's mailbox. Make sure the admin account you're using to connect to has App Impersonization permissions"
Write-Host "Check this link for more info: https://help.bittitan.com/hc/en-us/articles/115008098447-The-account-does-not-have-permission-to-impersonate-the-requested-user"
}
}

Error: Cannot find an overload for "restore" and the argument count: "1"

I am getting this error from the following code. It's coming from $Context.Load($RecycleBinItems). Any idea what's wrong with the code? I am attempting to restore all recyclebin items.
Add-Type -Path "C:\Program Files\WindowsPowerShell\Modules\SharePointPnPPowerShellOnline\3.17.2001.2\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\WindowsPowerShell\Modules\SharePointPnPPowerShellOnline\3.17.2001.2\Microsoft.SharePoint.Client.Runtime.dll"
Import-Module 'Microsoft.PowerShell.Security'
#Get the Site Owners Credentials to connect the SharePoint
$SiteUrl = "https://phaselinknet.sharepoint.com"
$UserName = Read-host "Enter the Email ID"
$Password = Read-host - assecurestring "Enter Password for $AdminUserName"
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName, $Password)
# Once Connected, get the Site information using current Context objects
Try {
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteUrl)
$Context.Credentials = $Credentials
$Site = $Context.Site
$RecycleBinItems = $Site.RecycleBin
$Context.Load($Site)
$Context.Load($RecycleBinItems)
$Context.ExecuteQuery()
Write-Host "Total Number of Files found in Recycle Bin:" $RecycleBinItems.Count
}
catch {
write - host "Error: $($_.Exception.Message)" - foregroundcolor Red
}
# using for loop to restore the item one by one
Try {
if($RecycleBinItems)
{
foreach($Item in $RecycleBinItems)
{
$Site.RecycleBin.restore($Item.ID)
#Write-Host "Item restored:"$Item.Title
}
}
}
catch {
write-host "Error: $($_.Exception.Message)" -foregroundcolor Red
}
The error message is giving you you answer. There is not a version of the method Restore that takes 1 parameter.
You need to load up a list of items simular to this
$Item = $RecycleBin | Where{$_.Title -eq $ItemName}
Then call restore for the items.
if($Item -ne $null)
{
$Item.Restore()
}
Thanks for the tip. So I load up the first 10 items in the recyclebin, and Write-Host does write out the correct files, but the $Item.Restore() does noting as the files are still not restored:
$itemsToRestore = #()
for ($i = 0; $i -lt 10; $i++)
{
$Item = $RecycleBinItems[$i]
$itemsToRestore += $Item
}
Write-Host "Total Number of Files to Restore:" $itemsToRestore.Count
foreach($item in $itemsToRestore)
{
Write-Host "Item:" $Item.Title
$item.Restore()
}
I found the problem. I missed $Context.ExecuteQuery() after $Item.Restore(). It works now.

do we have CSOM code to retrieve all lists under a site collection?

I am trying hard to get details of all the lists from a SharePoint site collection and export to CSV.
I am trying to run remotely from my machine and it doesn't work. I am not an export CSOM guy so asking for help, please.
I heard from someone that CSOM can run from anywhere and able to get info from SHarePoint 2013 server.
import-module C:\powershell\SharePointPnPPowerShell2013\3.16.1912.0\sharepointpnppowershell2013.psd1 -DisableNameChecking
$username = "testing#domain.com"
$password="Password"
$secureStringPwd = $Password | ConvertTo-SecureString -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $Username, $secureStringPwd
$connection=Connect-PnPOnline -Url https://cys.test.domain.com -Credentials $Credentials
$siteurl = "https://cys.test.domain.com/sites/testsite"
function Get-DocInventory([string]$siteUrl) {
$site = New-Object Microsoft.SharePoint.SPSite "https://cys.test.domain.com"
$web = Get-SPWeb "https://cys.test.domain.com/sites/testsite"
foreach ($list in $web.Lists) {
foreach ($item in $list.Items) {
#foreach($version in $item.Versions){
$data = #{
"List Name" = $list.Title
"Created By" = $item["Author"]
"Created Date" = $item["Created"]
"Modified By" = $item["Editor"]
"Modified Date" = $item["Modified"]
"Item Name" = $item.File.Name
"URL"=$web.Site.MakeFullUrl("$($web.ServerRelativeUrl.TrimEnd('/'))/$($item.Url)");
}
New-Object PSObject -Property $data | Select "List Name", "Item Name", "Created By", "Created Date", "Modified By", "Modified Date", "URL"
}
#}
$web.Dispose();
}
}
Get-DocInventory | Export-Csv -NoTypeInformation -Path "d:\test\test.csv"
If you are using SharePoint 2013 On-Premise, try to install SharePoint 2013 CSOM firstly:
SharePoint Server 2013 Client Components SDK
Then use this script to get list details in a site collection includes sub sites:
#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
#Variables for Processing
$SiteUrl= "http://sp/sites/devtest"
$username="administrator"
$password="******"
$domain="Contoso"
#Setup Credentials to connect
$Credentials = New-Object System.Net.NetworkCredential($username, $password, $domain)
Try {
#Function to Get all lists from the web
Function Get-SPOList($Web)
{
#Get All Lists from the web
$Lists = $Web.Lists
$Context.Load($Lists)
$Context.ExecuteQuery()
#Get all lists from the web
ForEach($List in $Lists)
{
#Get the List Name
Write-host $List.Title
Write-host $List.Created
}
}
#Function to get all webs from given URL
Function Get-SPOWeb($WebURL)
{
#Set up the context
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($WebURL)
$Context.Credentials = $Credentials
$Web = $context.Web
$Context.Load($web)
#Get all immediate subsites of the site
$Context.Load($web.Webs)
$Context.executeQuery()
#Call the function to Get Lists of the web
Write-host "Processing Web :"$Web.URL
Get-SPOList $Web
#Iterate through each subsite in the current web
foreach ($Subweb in $web.Webs)
{
#Call the function recursively to process all subsites underneaththe current web
Get-SPOWeb($SubWeb.URL)
}
}
#Call the function to get all sites
Get-SPOWeb $SiteUrl
}
catch {
write-host "Error: $($_.Exception.Message)" -foregroundcolor Red
}

migrate document with its meta data from one document library to a folder

I am beginner in sharepoint specially in powershell.
I am trying to move all the documents of a document library from one site to another site inside a folder. but I could just copy the files not metadata whcih come from contenttypes!
$PSSnapin = Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue Out-Null
clear
$org = "http://'YYYYY'/sites/XXXX"
$dest = "http://'YYYYY'/sites/XXXX/Subsite"
$orgLibrary = (Get-SPWeb $org).Folders["newdoc1"]
$destLibrary = (Get-SPWeb $dest).Folders["newdoc2"].SubFolders["folder1"]
$destFiles = $destLibrary.Files
foreach ($file in $orgLibrary.Files)
{
$curFile = $file.OpenBinary()
$destURL = $destFiles.Folder.Url + "/" + $file.Name
$destFiles.Add($destURL, $curFile, $true)
}
This one here does just that. Except for modified and created dates, all other fields are brought over.
$web = Get-SPWeb "http://sharepointed.com/"
$list = $web.Lists["Shared Documents"]
$spQuery = New-Object Microsoft.SharePoint.SPQuery
$spQuery.ViewAttributes = "Scope='Recursive'";
$spQuery.RowLimit = 2000
$caml = '<Where><Lt><FieldRef Name="Created" /><Value IncludeTimeValue="TRUE" Type="DateTime">2014-01-01T04:06:45Z</Value></Lt></Where> '
$spQuery.Query = $caml
do
{
$listItems = $list.GetItems($spQuery)
$spQuery.ListItemCollectionPosition = $listItems.ListItemCollectionPosition
$listTotal = $listItems.Count
for ($x=$listTotal-1;$x -ge 0; $x--)
{
try
{
$listItems[$x].CopyTo("http://sharepoint/Docs/Documents/"+ $listItems[$x].name)
Write-Host("DELETED: " + $listItems[$x].name)
$listItems[$x].Recycle()
}
catch
{
Write-Host $_.Exception.ToString()
}
}
}
while ($spQuery.ListItemCollectionPosition -ne $null)
via Ian

How to update SharePoint enterprise keywords with multiple values using powershell?

I'm trying to write a powershell script that looks at a user defined variable and updates the enterprise keywords for each item in a list that matches that value.
For instance, say you have a page in SP that has the managed metadata keywords: new, fresh, clean
I want a script that asks a user what keyword they want to swap out. So a user would specify a variable as: fresh and another variable as: fresher and it would update any item with the keyword fresh to fresher.
Here's what I 've used before but doesn't work now because there are mutliple values:
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA SilentlyContinue
$webURL = <MY SP URL>
$listName = <MY LIST NAME>
$web = Get-SPWeb $webURL
$list = $web.Lists[$listName]
$listitems = $list.items
$session = Get-SPTaxonomySession -Site $web.Site
$termStore = $session.TermStores["Managed Metadata Service"]
$group = $termStore.Groups["Resources"]
$termset = $group.TermSets["Wiki Categories"]
$terms = $termSet.GetTerms(100)
$wiki1 = read-host "Enter the wiki category you want to update"
$wiki2 = read-host "Enter the replacement wiki category"
$term = $terms | ?{$_.name -eq $wiki2}
Foreach($item in $listitems)
{$wiki = $item["Wiki Categories"]
if($wiki.label -eq $term)
{
$spitem = [Microsoft.SharePoint.SPListItem]$item;
$taxfield = [Microsoft.SharePoint.Taxonomy.TaxonomyField]$spitem.Fields["Wiki Categories"]
$taxfield.SetFieldValue($spitem, $term)
$spitem.Update()
$spitem.File.Publish("True")
}
}
I'm pretty sure the issue is with this line:
$term = $terms | ?{$_.name -eq $wiki2}
And this line:
$taxfield.SetFieldValue($spitem, $term)
The problem is that you passing a TermCollection ($term) to SetFieldValue where you should pass in a TaxonomyFieldValueCollection.
You can convert them like this:
$taxfield = $spitem.Fields["Wiki Categories"]
$tfvc = new-object -typename Microsoft.SharePoint.Taxonomy.TaxonomyFieldValueCollection -argumentlist $taxfield;
foreach($t in $ term)
{
$tfv = new-object -typename Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue -argumentlist $taxfield
$tfv.TermGuid = $t.Id
$tfv.Label = $t.Name
$tfvc.Add($tfv)
}
...
$taxfield.SetFieldValue($spitem, $tfvc)

Resources