<# .Synopsis Use Copy-SharepointLibrary para copiar una biblioteca de documentos a disco. .Description Este script itera recursivamente sobre todos los directorios y ficheros de una biblioteca de documentos de Sharepoint 2010 y escribe los datos a disco. La estructura se mantiene igual que en la biblioteca de documentos. Se puede indicar únicamente que se exporte una carpeta de la biblioteca de documentos. El proceso registrará su actividad en un archivo de log (o en consola, según el valor de la variable LogToConsole). .Parameter Url Especifica el sitio web de Sharepoint que contiene la biblioteca de documentos. .Parameter LibraryName Especifica el nombre de la biblioteca de documentos. .Parameter Path Especifica la ruta en la que se replicará el contenido de la biblioteca de documentos. .Parameter RemoteFolder Especifica la carpeta remota a partir de la cual extraer los ficheros de la biblioteca de documentos. El valor indicado debe comenzar con el nombre de la biblioteca, no su título. .Parameter Security Indica si se deben replicar los permisos de los elementos copiados. .Example PS> .\Copy-SharepointLibrary.ps1 -Url "http://spWeb" -LibraryName "Documentos" -Path "\\repositorio\documentos" -RemoteFile "Documents/carpeta1" .Notes Autor: Gustavo Carou Actualizado: 2020-10-01 #> [CmdletBinding()] param( [Parameter(Mandatory=$true)][string] $Url, [Parameter(Mandatory=$true)][string] $LibraryName, [Parameter(Mandatory=$true)][string] $Path, [Parameter(Mandatory=$false)][string] $RemoteFolder, [bool] $Security = $true ) # Definición de variables y constantes ------------------------------------------------------------- # Constantes y variables para logs Set-Variable -Name LogLevelDebug -Option Constant -Value 4 Set-Variable -Name LogLevelInfo -Option Constant -Value 3 Set-Variable -Name LogLevelWarn -Option Constant -Value 2 Set-Variable -Name LogLevelError -Option Constant -Value 1 Set-Variable -Name LogLevelFatal -Option Constant -Value 0 # Establece el nivel de mensajes a mostrar Set-Variable -Name LogLevel -Option ReadOnly -Value $LogLevelDebug # Si es true, solo escribe en consola Set-Variable -Name LogToConsole -Option ReadOnly -Value $false Set-Variable -Name NoOpExitCode -Option Constant -Value -1 Set-Variable -Name ScriptName -Option ReadOnly -Value $MyInvocation.MyCommand.Name # Variables de progreso $global:contador = 0 $global:total = 0 # Funciones ---------------------------------------------------------------------------------------- function Get-ColorMessage { <# .Description Obtiene el color de texto para el nivel de mensaje #> param( # Clasificador del mensaje [string] $Level ) switch ($Level) { "INFO" { return "White" } "WARN" { return "Blue" } "ERROR" { return "Yellow" } "FATAL" { return "Red" } "DEBUG" { return "Gray" } Default { return "White" } } } function Get-LogLevel { <# .Description Devuelve la prioridad que corresponde con el nivel de log #> param( # Clasificador del mensaje [string] $Level) switch ($Level) { "INFO" { return $LogLevelInfo } "WARN" { return $LogLevelWarn } "ERROR" { return $LogLevelError } "FATAL" { return $LogLevelFatal } "DEBUG" { return $LogLevelDebug } Default { return $LogLevelInfo } } } function Get-Log { <# .Description Devuelve el nombre del fichero de log #> $fileName = "export-" + (Get-Date).ToString("yyyyMMdd") + ".log" return Join-Path $PWD.Path $fileName } function Write-Log { <# .Description Escribe un mensaje en pantalla o en el fichero de log dependiendo de la salida elegida, siempre y cuando el clasificador del mensaje tenga una prioridad igual o mayor que la establecida en la variable LogLevel #> param( [Parameter(Mandatory = $false)] [ValidateSet("INFO", "WARN", "ERROR", "FATAL", "DEBUG")] [string] # Clasificador del mensaje $Level = "INFO", [Parameter(Mandatory = $true)] [string] # Mensaje que se añadirá a la traza $Message ) $messageLevel = Get-LogLevel $Level $stamp = (Get-Date).ToString("HH:mm:ss.fff") $line = "$stamp $Level $Message" # Sólo se escriben en fichero los mensajes con prioridad igual o mayor que la establecida por $LogLevel if($LogLevel -lt $messageLevel) { return } if($LogToConsole) { $color = Get-ColorMessage -Level $Level Write-Host $line -ForegroundColor $color } else { $logFile = Get-Log if($logFile) { try { Add-Content $logFile -Value $line } catch { Write-Host $line Write-Host -ForegroundColor Red "Error al escribir en log: " + $_ } } # Los mensajes que no son de depuración, se muestran en pantalla. if($messageLevel -lt $LogLevelDebug) { $color = Get-ColorMessage -Level $Level Write-Host "$stamp $Message" -ForegroundColor $color } } } function Contar { <# .Description Cuenta los ficheros que se exportarán #> param([Microsoft.SharePoint.SPFolder] $Folder) if($Folder.Name -eq "Forms") { return } $global:total += $Folder.Files.Count if($Folder.SubFolders) { $Folder.SubFolders | ForEach-Object { Contar $_ } } } function Progress { <# .Description Actualiza la barra de progreso del proceso de exportación #> param([string] $Path) $global:contador++ $porcentaje = $global:contador / $global:total * 100 Write-Progress -Activity "Exportando documentos de $Library" -Status $Path -PercentComplete $porcentaje if($porcentaje -gt 0 -and $porcentaje % 10 -eq 0) { Write-Log -Level "INFO" -Message "$global:contador de $global:total archivos exportados" } } function TrimFolder { <# .Description Quita el \ final de una ruta #> param([string] $Path) if ($Path.EndsWith("\")) { $Path = $Path.Substring(0, ($Path.Length - 1)) } return $Path } function EnsureFolder { <# .Description La función se asegura de que la ruta exista en el sistema de ficheros #> param([string] $Path) if(Test-Path $Path) { return } try { New-Item -Path $Path -ItemType Directory Write-Log -Level "DEBUG" -Message "Nueva carpeta: $Path" } catch { Write-Log -Level "FATAL" -Message "No se ha podido crear la ruta $Path" exit $NoOpExitCode } } function SaveFile { <# .Description Guarda un fichero en el sistema de ficheros #> param ( # Objeto fichero que se guardará $File, # Ruta en la que se guardará el fichero [string] $Path ) $filePath = Join-Path $Path $File.Name try { $binary = $File.OpenBinary() [System.IO.File]::WriteAllBytes($filePath, $binary) Progress -Path $filePath } catch { Write-Log -Level "INFO" -Message "Error al copiar $filePath" Write-Log -Level "ERROR" -Message $Error[0] } } function ExportFiles { <# .Description Exporta los ficheros de una carpeta de una biblioteca de documentos de forma recursiva recreando la estructura de directorios en el sistema de ficheros #> param( # Sitio web que contiene la carpeta a exportar [Microsoft.SharePoint.SPWeb] $Site, # Carpeta a exportar [Microsoft.SharePoint.SPFolder] $Folder ) $files = $Folder.Files if($files) { foreach ($file in $files) { $destination = Join-Path $Path $Folder.Url EnsureFolder -Path $destination SaveFile -Path $destination -File $file } } $subFolders = $Folder.SubFolders | Where-Object { $_.Name -ne "Forms" } if($subFolders) { foreach ($subFolder in $subFolders) { ExportFiles -Site $Site -Folder $subFolder } } } function Get-InitialFolder { <# .Description Obtiene el objeto SPFolder que se corresponde con la url de la carpeta indicada. #> param( # Carpeta desde la que buscar [Microsoft.Sharepoint.SPFolder] $Parent, # URL de la carpeta [string] $FolderUrl ) [Microsoft.SharePoint.SPFolder] $result = $null if($Parent.Url -eq $FolderUrl) { return $Parent } $childFolder = $Parent.SubFolders | Where-Object { $FolderUrl.StartsWith($_.Url) } | Select-Object -first 1 if($null -eq $childFolder) { return $null } return Get-InitialFolder -Parent $childFolder -FolderUrl $FolderUrl } function Get-Permissions { # TODO: Obtener los permisos efectivos sobre un fichero de la librería de documentos. } function Set-Permissions { # TODO: Establecer los permisos equivalentes en el sistema de ficheros para el fichero copiado. } # Inicio de ejecución ----------------------------------------------------------------- $snapin = (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) if($null -eq $snapin) { Write-Host -ForegroundColor Gray "No se ha encontrado el snap-in de SharePoint... Cargando ahora" Add-PSSnapin Microsoft.SharePoint.PowerShell } if ([string]::IsNullOrEmpty($Url) -or [string]::IsNullOrEmpty($LibraryName) -or [string]::IsNullOrEmpty($Path)) { $script = Join-Path $PWD $MyInvocation.MyCommand.Name Get-Help $script -Detailed exit $NoOpExitCode } #Habilita los mensajes de depuración (en objetos de terceros) #$DebugPreference = "Continue" Write-Log -Level "DEBUG" -Message "`r`nSitio: $Url`r`nBiblioteca: $LibraryName`r`nDestino: $Path`r`nCarpeta remota: $RemoteFolder" $Path = TrimFolder -Path $Path EnsureFolder -Path $Path try { [Microsoft.SharePoint.SPSite] $Site = Get-SPSite -Identity $Url [Microsoft.SharePoint.SPWeb] $Web = Get-SPWeb $Url [Microsoft.SharePoint.SPList] $Library = $null [Microsoft.SharePoint.SPFolder] $initialFolder = $null if($Web) { $Library = $Web.Lists | Where-Object {$_.Title -eq $LibraryName} | Select-Object -first 1 } else { Write-Log -Level "FATAL" -Message "No se ha podido acceder a la web" exit $NoOpExitCode } if($Library) { Write-Log -Level "DEBUG" -Message $Library.RootFolder.Url } else { Write-Log -Level "FATAL" -Message "No se ha encontrado la librería" exit $NoOpExitCode } if([string]::IsNullOrEmpty($RemoteFolder)) { # Si no se indica una carpeta remota, se exporta desde la raíz de la biblioteca. $initialFolder = $Library.RootFolder } else { $initialFolder = Get-InitialFolder -Parent $Library.RootFolder -FolderUrl $RemoteFolder if($null -eq $initialFolder) { Write-Log -Level "FATAL" -Message "No se ha encontrado la carpeta $RemoteFolder. Recuerde que la ruta debe comenzar con el nombre de la biblioteca (no su título)." exit $NoOpExitCode } } Write-Log -Level "INFO" -Message "Contando ficheros, por favor espere..." Contar $initialFolder Write-Log -Level "INFO" -Message "$global:total ficheros a exportar." if($global:total -gt 0) { ExportFiles -Site $Web -Folder $initialFolder } } catch { Write-Log -Level "FATAL" -Message $Error[0] } finally { if($Web) { $Web.Dispose() } if($Site) { $Site.Dispose() } }