<#
.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 <[email protected]>
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()
}
}
Comments