5 Commits

Author SHA1 Message Date
a1edea3007 Cleanup2
All checks were successful
Release zip package / release (push) Successful in 1s
2026-03-17 10:36:59 +01:00
db0f87dc00 Cleanup 2026-03-17 10:36:41 +01:00
28e4c21e80 Added version cleanup feature 2026-03-17 10:35:39 +01:00
5c5e4b1415 Buttons size fix 2026-03-16 16:55:27 +01:00
086804edf9 Added functionnality : you can vcreate a whole folder tree by importing a CSV (see examples fodler) 2026-03-16 16:39:37 +01:00
3 changed files with 540 additions and 2 deletions

View File

@@ -2854,6 +2854,32 @@ $script:LangDefault = @{
"bulk.status.creating" = "Creating..." "bulk.status.creating" = "Creating..."
"bulk.status.ok" = "OK" "bulk.status.ok" = "OK"
"bulk.status.error" = "Error" "bulk.status.error" = "Error"
"tab.structure" = " Structure "
"grp.struct.csv" = "CSV Import"
"lbl.struct.desc" = "Import a CSV to create a folder tree. Each column represents a depth level."
"btn.struct.csv" = "Load CSV..."
"grp.struct.preview" = "Preview"
"grp.struct.target" = "Target"
"lbl.struct.library" = "Target library:"
"ph.struct.library" = "Shared Documents"
"btn.struct.create" = "Create Structure"
"btn.struct.clear" = "Clear"
"struct.col.path" = "Full Path"
"struct.col.depth" = "Depth"
"tab.versions" = " Versions "
"grp.ver.keep" = "Versions to Keep"
"lbl.ver.count" = "Number of versions to keep:"
"chk.ver.date" = "Also filter by date"
"rad.ver.before" = "Keep versions before:"
"rad.ver.after" = "Keep versions after:"
"grp.ver.scope" = "Scope"
"lbl.ver.library" = "Library / Folder:"
"ph.ver.library" = "Shared Documents"
"chk.ver.recursive" = "Include subfolders (recursive)"
"chk.ver.subsites" = "Include subsites"
"chk.ver.dryrun" = "Dry run (preview only, no deletion)"
"btn.ver.run" = "Clean Versions"
"btn.ver.open" = "Open Report"
} }
$script:Lang = $null # null = use LangDefault $script:Lang = $null # null = use LangDefault
@@ -3606,7 +3632,137 @@ $btnBulkCreate.Size = New-Object System.Drawing.Size(200, 34)
$tabBulk.Controls.AddRange(@($grpBulkList, $btnBulkCreate)) $tabBulk.Controls.AddRange(@($grpBulkList, $btnBulkCreate))
$tabs.TabPages.AddRange(@($tabPerms, $tabStorage, $tabTemplates, $tabSearch, $tabDupes, $tabTransfer, $tabBulk)) # ══════════════════════════════════════════════════════════════════════════════
# Tab 8 Structure (folder tree from CSV)
# ══════════════════════════════════════════════════════════════════════════════
$tabStruct = New-Object System.Windows.Forms.TabPage
$tabStruct.Text = T "tab.structure"
# ── CSV import + target (single row) ───────────────────────────────────────
$grpStructCsv = New-Group (T "grp.struct.csv") 10 4 620 52
$lblStructDesc = New-Object System.Windows.Forms.Label
$lblStructDesc.Text = T "lbl.struct.desc"
$lblStructDesc.Location = New-Object System.Drawing.Point(10, 20)
$lblStructDesc.Size = New-Object System.Drawing.Size(460, 20)
$btnStructCsv = New-Object System.Windows.Forms.Button
$btnStructCsv.Text = T "btn.struct.csv"
$btnStructCsv.Location = New-Object System.Drawing.Point(490, 18)
$btnStructCsv.Size = New-Object System.Drawing.Size(118, 26)
$grpStructCsv.Controls.AddRange(@($lblStructDesc, $btnStructCsv))
# ── Preview ────────────────────────────────────────────────────────────────
$grpStructPreview = New-Group (T "grp.struct.preview") 10 58 620 148
$tvStruct = New-Object System.Windows.Forms.TreeView
$tvStruct.Location = New-Object System.Drawing.Point(10, 18)
$tvStruct.Size = New-Object System.Drawing.Size(598, 120)
$tvStruct.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$tvStruct.ShowLines = $true
$tvStruct.ShowPlusMinus = $true
$grpStructPreview.Controls.Add($tvStruct)
# ── Target + Buttons (single row) ─────────────────────────────────────────
$lblStructLib = New-Object System.Windows.Forms.Label
$lblStructLib.Text = T "lbl.struct.library"
$lblStructLib.Location = New-Object System.Drawing.Point(12, 214)
$lblStructLib.Size = New-Object System.Drawing.Size(110, 20)
$txtStructLib = New-Object System.Windows.Forms.TextBox
$txtStructLib.Location = New-Object System.Drawing.Point(124, 212)
$txtStructLib.Size = New-Object System.Drawing.Size(200, 22)
$txtStructLib.PlaceholderText = T "ph.struct.library"
$btnStructCreate = New-ActionBtn (T "btn.struct.create") 340 208 ([System.Drawing.Color]::FromArgb(0, 120, 212))
$btnStructCreate.Size = New-Object System.Drawing.Size(180, 30)
$btnStructClear = New-Object System.Windows.Forms.Button
$btnStructClear.Text = T "btn.struct.clear"
$btnStructClear.Location = New-Object System.Drawing.Point(528, 208)
$btnStructClear.Size = New-Object System.Drawing.Size(90, 30)
$tabStruct.Controls.AddRange(@($grpStructCsv, $grpStructPreview, $lblStructLib, $txtStructLib, $btnStructCreate, $btnStructClear))
# ══════════════════════════════════════════════════════════════════════════════
# Tab 9 Version Cleanup
# ══════════════════════════════════════════════════════════════════════════════
$tabVersions = New-Object System.Windows.Forms.TabPage
$tabVersions.Text = T "tab.versions"
$tabVersions.BackColor = [System.Drawing.Color]::WhiteSmoke
# ── Versions to keep ─────────────────────────────────────────────────────────
$grpVerKeep = New-Group (T "grp.ver.keep") 10 4 620 110
$lblVerCount = New-Object System.Windows.Forms.Label
$lblVerCount.Text = T "lbl.ver.count"
$lblVerCount.Location = New-Object System.Drawing.Point(10, 22)
$lblVerCount.Size = New-Object System.Drawing.Size(220, 20)
$nudVerCount = New-Object System.Windows.Forms.NumericUpDown
$nudVerCount.Location = New-Object System.Drawing.Point(235, 20)
$nudVerCount.Size = New-Object System.Drawing.Size(70, 22)
$nudVerCount.Minimum = 0
$nudVerCount.Maximum = 500
$nudVerCount.Value = 5
$chkVerDate = New-Check (T "chk.ver.date") 10 50 250 $false
$radVerBefore = New-Radio (T "rad.ver.before") 30 74 200 $true
$radVerBefore.Enabled = $false
$radVerAfter = New-Radio (T "rad.ver.after") 30 96 200 $false
$radVerAfter.Enabled = $false
$dtpVer = New-Object System.Windows.Forms.DateTimePicker
$dtpVer.Location = New-Object System.Drawing.Point(235, 74)
$dtpVer.Size = New-Object System.Drawing.Size(150, 22)
$dtpVer.Format = [System.Windows.Forms.DateTimePickerFormat]::Short
$dtpVer.Enabled = $false
$chkVerDate.Add_CheckedChanged({
$on = $chkVerDate.Checked
$radVerBefore.Enabled = $on
$radVerAfter.Enabled = $on
$dtpVer.Enabled = $on
})
$grpVerKeep.Controls.AddRange(@($lblVerCount, $nudVerCount, $chkVerDate, $radVerBefore, $radVerAfter, $dtpVer))
# ── Scope ─────────────────────────────────────────────────────────────────────
$grpVerScope = New-Group (T "grp.ver.scope") 10 118 620 76
$lblVerLib = New-Object System.Windows.Forms.Label
$lblVerLib.Text = T "lbl.ver.library"
$lblVerLib.Location = New-Object System.Drawing.Point(10, 22)
$lblVerLib.Size = New-Object System.Drawing.Size(150, 20)
$txtVerLib = New-Object System.Windows.Forms.TextBox
$txtVerLib.Location = New-Object System.Drawing.Point(164, 20)
$txtVerLib.Size = New-Object System.Drawing.Size(230, 22)
$txtVerLib.PlaceholderText = T "ph.ver.library"
$chkVerRecursive = New-Check (T "chk.ver.recursive") 10 48 260 $true
$chkVerSubsites = New-Check (T "chk.ver.subsites") 280 48 200 $false
$grpVerScope.Controls.AddRange(@($lblVerLib, $txtVerLib, $chkVerRecursive, $chkVerSubsites))
# ── Options + Buttons ─────────────────────────────────────────────────────────
$chkVerDryRun = New-Check (T "chk.ver.dryrun") 12 200 350 $true
$btnVerRun = New-ActionBtn (T "btn.ver.run") 10 228 ([System.Drawing.Color]::FromArgb(180, 60, 20))
$btnVerRun.Size = New-Object System.Drawing.Size(180, 30)
$btnVerOpen = New-Object System.Windows.Forms.Button
$btnVerOpen.Text = T "btn.ver.open"
$btnVerOpen.Location = New-Object System.Drawing.Point(200, 228)
$btnVerOpen.Size = New-Object System.Drawing.Size(130, 30)
$btnVerOpen.Enabled = $false
$tabVersions.Controls.AddRange(@($grpVerKeep, $grpVerScope, $chkVerDryRun, $btnVerRun, $btnVerOpen))
$tabs.TabPages.AddRange(@($tabPerms, $tabStorage, $tabTemplates, $tabSearch, $tabDupes, $tabTransfer, $tabBulk, $tabStruct, $tabVersions))
# ── Progress bar ─────────────────────────────────────────────────────────────── # ── Progress bar ───────────────────────────────────────────────────────────────
$progressBar = New-Object System.Windows.Forms.ProgressBar $progressBar = New-Object System.Windows.Forms.ProgressBar
@@ -3793,6 +3949,24 @@ $_reg = {
& $_reg $script:i18nMap $btnBulkRemove "btn.bulk.remove" & $_reg $script:i18nMap $btnBulkRemove "btn.bulk.remove"
& $_reg $script:i18nMap $btnBulkClear "btn.bulk.clear" & $_reg $script:i18nMap $btnBulkClear "btn.bulk.clear"
& $_reg $script:i18nMap $btnBulkCreate "btn.bulk.create" & $_reg $script:i18nMap $btnBulkCreate "btn.bulk.create"
& $_reg $script:i18nMap $lblStructDesc "lbl.struct.desc"
& $_reg $script:i18nMap $btnStructCsv "btn.struct.csv"
& $_reg $script:i18nMap $lblStructLib "lbl.struct.library"
& $_reg $script:i18nMap $btnStructCreate "btn.struct.create"
& $_reg $script:i18nMap $btnStructClear "btn.struct.clear"
# Version Cleanup tab
& $_reg $script:i18nMap $lblVerCount "lbl.ver.count"
& $_reg $script:i18nMap $chkVerDate "chk.ver.date"
& $_reg $script:i18nMap $radVerBefore "rad.ver.before"
& $_reg $script:i18nMap $radVerAfter "rad.ver.after"
& $_reg $script:i18nMap $lblVerLib "lbl.ver.library"
& $_reg $script:i18nMap $chkVerRecursive "chk.ver.recursive"
& $_reg $script:i18nMap $chkVerSubsites "chk.ver.subsites"
& $_reg $script:i18nMap $chkVerDryRun "chk.ver.dryrun"
& $_reg $script:i18nMap $btnVerRun "btn.ver.run"
& $_reg $script:i18nMap $btnVerOpen "btn.ver.open"
& $_reg $script:i18nMap $grpVerKeep "grp.ver.keep"
& $_reg $script:i18nMap $grpVerScope "grp.ver.scope"
# Tab pages # Tab pages
& $_reg $script:i18nTabs $tabPerms "tab.perms" & $_reg $script:i18nTabs $tabPerms "tab.perms"
@@ -3802,6 +3976,8 @@ $_reg = {
& $_reg $script:i18nTabs $tabDupes "tab.dupes" & $_reg $script:i18nTabs $tabDupes "tab.dupes"
& $_reg $script:i18nTabs $tabTransfer "tab.transfer" & $_reg $script:i18nTabs $tabTransfer "tab.transfer"
& $_reg $script:i18nTabs $tabBulk "tab.bulk" & $_reg $script:i18nTabs $tabBulk "tab.bulk"
& $_reg $script:i18nTabs $tabStruct "tab.structure"
& $_reg $script:i18nTabs $tabVersions "tab.versions"
# Menu items # Menu items
& $_reg $script:i18nMenus $menuSettings "menu.settings" & $_reg $script:i18nMenus $menuSettings "menu.settings"
@@ -3816,10 +3992,12 @@ $script:i18nPlaceholders = [System.Collections.Generic.Dictionary[string,object]
& $_reg $script:i18nPlaceholders $txtSrchModBy "ph.modified.by" & $_reg $script:i18nPlaceholders $txtSrchModBy "ph.modified.by"
& $_reg $script:i18nPlaceholders $txtSrchLib "ph.library" & $_reg $script:i18nPlaceholders $txtSrchLib "ph.library"
& $_reg $script:i18nPlaceholders $txtDupLib "ph.dup.lib" & $_reg $script:i18nPlaceholders $txtDupLib "ph.dup.lib"
& $_reg $script:i18nPlaceholders $txtStructLib "ph.struct.library"
& $_reg $script:i18nPlaceholders $txtXferSrcSite "ph.xfer.site" & $_reg $script:i18nPlaceholders $txtXferSrcSite "ph.xfer.site"
& $_reg $script:i18nPlaceholders $txtXferSrcLib "ph.xfer.library" & $_reg $script:i18nPlaceholders $txtXferSrcLib "ph.xfer.library"
& $_reg $script:i18nPlaceholders $txtXferDstSite "ph.xfer.site" & $_reg $script:i18nPlaceholders $txtXferDstSite "ph.xfer.site"
& $_reg $script:i18nPlaceholders $txtXferDstLib "ph.xfer.library" & $_reg $script:i18nPlaceholders $txtXferDstLib "ph.xfer.library"
& $_reg $script:i18nPlaceholders $txtVerLib "ph.ver.library"
#endregion #endregion
@@ -5578,6 +5756,318 @@ $btnBulkCreate.Add_Click({
#endregion #endregion
#region ===== Structure (folder tree from CSV) =====
# Store the parsed folder paths
$script:_StructPaths = @()
function Build-StructTree([string]$csvPath) {
# Auto-detect delimiter
$raw = Get-Content $csvPath -Raw
$delim = if ($raw -match ';') { ';' } else { ',' }
$rows = Import-Csv $csvPath -Delimiter $delim
$paths = [System.Collections.Generic.List[string]]::new()
foreach ($r in $rows) {
$cols = @($r.PSObject.Properties | ForEach-Object { "$($_.Value)".Trim() })
# Build path from non-empty columns
$parts = @($cols | Where-Object { $_ -ne '' })
if ($parts.Count -gt 0) {
# Add all intermediate paths to ensure parents exist
for ($i = 1; $i -le $parts.Count; $i++) {
$p = ($parts[0..($i-1)] -join '/')
if (-not $paths.Contains($p)) { $paths.Add($p) }
}
}
}
$script:_StructPaths = @($paths | Sort-Object)
return $script:_StructPaths
}
function Populate-StructTreeView([string[]]$paths) {
$tvStruct.Nodes.Clear()
$nodeMap = @{}
foreach ($p in $paths) {
$parts = $p -split '/'
$parentKey = if ($parts.Count -gt 1) { ($parts[0..($parts.Count - 2)] -join '/') } else { '' }
$name = $parts[-1]
$node = New-Object System.Windows.Forms.TreeNode($name)
$node.Tag = $p
if ($parentKey -and $nodeMap.ContainsKey($parentKey)) {
$nodeMap[$parentKey].Nodes.Add($node) | Out-Null
} else {
$tvStruct.Nodes.Add($node) | Out-Null
}
$nodeMap[$p] = $node
}
$tvStruct.ExpandAll()
}
$btnStructCsv.Add_Click({
$ofd = New-Object System.Windows.Forms.OpenFileDialog
$ofd.Filter = "CSV (*.csv)|*.csv|All (*.*)|*.*"
if ($ofd.ShowDialog($form) -ne "OK") { return }
try {
$paths = Build-StructTree $ofd.FileName
Populate-StructTreeView $paths
Write-Log "$($paths.Count) folder(s) loaded from CSV." "LightGreen"
} catch {
Write-Log "CSV error: $($_.Exception.Message)" "Red"
}
})
$btnStructClear.Add_Click({
$tvStruct.Nodes.Clear()
$script:_StructPaths = @()
Write-Log "Structure cleared." "Gray"
})
$btnStructCreate.Add_Click({
$siteUrl = $txtSiteUrl.Text.Trim()
$clientId = $txtClientId.Text.Trim()
$library = $txtStructLib.Text.Trim()
if (-not $siteUrl) { Write-Log "Site URL required." "Red"; return }
if (-not $clientId) { Write-Log "Client ID required." "Red"; return }
if (-not $library) { Write-Log "Target library required." "Red"; return }
if ($script:_StructPaths.Count -eq 0) { Write-Log "No structure loaded. Load a CSV first." "Red"; return }
$btnStructCreate.Enabled = $false
$btnStructCsv.Enabled = $false
Start-ProgressAnim
Write-Log "=== CREATING FOLDER STRUCTURE ===" "White"
Write-Log "Target: $siteUrl / $library" "Gray"
Write-Log "Folders to create: $($script:_StructPaths.Count)" "Gray"
Write-Log ("-" * 52) "DarkGray"
try {
Connect-PnPOnline -Url $siteUrl -Interactive -ClientId $clientId
# Get the library root
$list = Get-PnPList -Identity $library -ErrorAction Stop
$rf = Get-PnPProperty -ClientObject $list -Property RootFolder
$base = $rf.ServerRelativeUrl.TrimEnd('/')
$ok = 0
$err = 0
$total = $script:_StructPaths.Count
foreach ($p in $script:_StructPaths) {
$folderPath = "$base/$p"
try {
# Resolve-PnPFolder creates the full path recursively
Resolve-PnPFolder -SiteRelativePath "$library/$p" -ErrorAction Stop | Out-Null
$ok++
Write-Log " OK: $p" "LightGreen"
} catch {
$err++
Write-Log " FAIL: $p$($_.Exception.Message)" "Red"
}
}
Write-Log "=== STRUCTURE COMPLETE: $ok OK, $err error(s) ===" "White"
} catch {
Write-Log "Error: $($_.Exception.Message)" "Red"
} finally {
$btnStructCreate.Enabled = $true
$btnStructCsv.Enabled = $true
Stop-ProgressAnim
}
})
# ── Version Cleanup handlers ─────────────────────────────────────────────────
$script:_VerReport = $null
$btnVerOpen.Add_Click({
if ($script:_VerReport -and (Test-Path $script:_VerReport)) {
Start-Process $script:_VerReport
}
})
$btnVerRun.Add_Click({
# --- Gather all selected site URLs ---
$siteUrls = @()
if ($script:_CachedSites -and $script:_CachedSites.Count -gt 0) {
foreach ($s in $script:_CachedSites) {
if ($s.Checked) { $siteUrls += $s.Url }
}
}
if ($siteUrls.Count -eq 0) {
$single = $txtSiteUrl.Text.Trim()
if ($single) { $siteUrls = @($single) }
}
if ($siteUrls.Count -eq 0) { Write-Log "Site URL required." "Red"; return }
$clientId = $txtClientId.Text.Trim()
if (-not $clientId) { Write-Log "Client ID required." "Red"; return }
$keepCount = [int]$nudVerCount.Value
$useDate = $chkVerDate.Checked
$dateBefore = $radVerBefore.Checked # true = keep before, false = keep after
$cutoffDate = $dtpVer.Value
$library = $txtVerLib.Text.Trim()
$recursive = $chkVerRecursive.Checked
$subsites = $chkVerSubsites.Checked
$dryRun = $chkVerDryRun.Checked
$btnVerRun.Enabled = $false
Start-ProgressAnim
$modeLabel = if ($dryRun) { "DRY RUN" } else { "LIVE" }
Write-Log "=== VERSION CLEANUP ($modeLabel) ===" "White"
Write-Log "Keep: $keepCount version(s)" "Gray"
if ($useDate) {
$dir = if ($dateBefore) { "before" } else { "after" }
Write-Log "Date filter: keep versions $dir $($cutoffDate.ToString('yyyy-MM-dd'))" "Gray"
}
Write-Log ("-" * 52) "DarkGray"
$report = [System.Collections.Generic.List[object]]::new()
$totalDeleted = 0
$totalKept = 0
$totalErrors = 0
try {
foreach ($siteUrl in $siteUrls) {
Write-Log "Connecting to $siteUrl ..." "Gray"
Connect-PnPOnline -Url $siteUrl -Interactive -ClientId $clientId
# Collect site URLs to process (main + subsites)
$sitesToProcess = @($siteUrl)
if ($subsites) {
try {
$subs = Get-PnPSubWeb -Recurse -ErrorAction SilentlyContinue
foreach ($sw in $subs) { $sitesToProcess += $sw.Url }
} catch {}
}
foreach ($currentSite in $sitesToProcess) {
if ($currentSite -ne $siteUrl) {
try { Connect-PnPOnline -Url $currentSite -Interactive -ClientId $clientId } catch {
Write-Log " Cannot connect to subsite $currentSite — skipped" "DarkOrange"
continue
}
}
Write-Log "Processing site: $currentSite" "White"
# Get target lists
$lists = @()
if ($library) {
try { $lists = @(Get-PnPList -Identity $library -ErrorAction Stop) } catch {
Write-Log " Library '$library' not found — skipped" "DarkOrange"
continue
}
} else {
$lists = Get-PnPList | Where-Object { $_.BaseTemplate -eq 101 -and $_.Hidden -eq $false }
}
foreach ($list in $lists) {
Write-Log " Library: $($list.Title)" "Gray"
try {
$camlQuery = "<View Scope='RecursiveAll'><Query></Query><RowLimit>5000</RowLimit></View>"
if (-not $recursive) {
$camlQuery = "<View><Query></Query><RowLimit>5000</RowLimit></View>"
}
$items = Get-PnPListItem -List $list.Title -Query $camlQuery -ErrorAction Stop |
Where-Object { $_.FileSystemObjectType -eq "File" }
} catch {
Write-Log " Error listing files: $($_.Exception.Message)" "Red"
$totalErrors++
continue
}
foreach ($item in $items) {
try {
$file = $item.FieldValues["FileRef"]
$versions = Get-PnPFileVersion -Url $file -ErrorAction Stop
if ($versions.Count -le $keepCount) { continue }
# Sort versions oldest first (by VersionLabel numeric)
$sorted = $versions | Sort-Object { [double]$_.VersionLabel }
# Determine which versions to delete
$toDelete = @()
foreach ($v in $sorted) {
# Always keep the last $keepCount versions
$idx = [array]::IndexOf($sorted, $v)
$remaining = $sorted.Count - $idx
if ($remaining -le $keepCount) { break }
# Apply date filter if enabled
if ($useDate) {
$vDate = [datetime]$v.Created
if ($dateBefore) {
# Keep versions before cutoff → delete versions ON or AFTER cutoff
if ($vDate -lt $cutoffDate) { continue }
} else {
# Keep versions after cutoff → delete versions BEFORE cutoff
if ($vDate -ge $cutoffDate) { continue }
}
}
$toDelete += $v
}
if ($toDelete.Count -eq 0) { continue }
$fileName = Split-Path $file -Leaf
foreach ($v in $toDelete) {
if ($dryRun) {
Write-Log " [DRY] Would delete v$($v.VersionLabel) of $fileName ($($v.Created))" "DarkOrange"
} else {
try {
Remove-PnPFileVersion -Url $file -Identity $v.Id -Force -ErrorAction Stop
Write-Log " Deleted v$($v.VersionLabel) of $fileName" "LightGreen"
} catch {
Write-Log " Error deleting v$($v.VersionLabel) of $fileName$($_.Exception.Message)" "Red"
$totalErrors++
}
}
$totalDeleted++
}
$kept = $sorted.Count - $toDelete.Count
$totalKept += $kept
$report.Add([PSCustomObject]@{
Site = $currentSite
Library = $list.Title
File = $file
TotalVer = $sorted.Count
Deleted = $toDelete.Count
Kept = $kept
})
} catch {
$totalErrors++
}
}
}
}
}
# Export CSV report
if ($report.Count -gt 0) {
$outDir = $txtOutput.Text.Trim()
if (-not $outDir) { $outDir = if ($PSScriptRoot) { $PSScriptRoot } else { $PWD.Path } }
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir | Out-Null }
$stamp = Get-Date -Format "yyyyMMdd_HHmmss"
$prefix = if ($dryRun) { "VersionCleanup_DryRun" } else { "VersionCleanup" }
$csvFile = Join-Path $outDir "${prefix}_$stamp.csv"
$report | Export-Csv -Path $csvFile -NoTypeInformation -Encoding UTF8
$script:_VerReport = $csvFile
$btnVerOpen.Enabled = $true
Write-Log "Report: $csvFile" "White"
}
Write-Log "=== VERSION CLEANUP COMPLETE: $totalDeleted deleted, $totalKept kept, $totalErrors error(s) ===" "White"
} catch {
Write-Log "Error: $($_.Exception.Message)" "Red"
} finally {
$btnVerRun.Enabled = $true
Stop-ProgressAnim
}
})
#endregion
# ── Initialisation : chargement des settings ─────────────────────────────── # ── Initialisation : chargement des settings ───────────────────────────────
$_settings = Load-Settings $_settings = Load-Settings
$script:DataFolder = if ($_settings.dataFolder -and (Test-Path $_settings.dataFolder)) { $script:DataFolder = if ($_settings.dataFolder -and (Test-Path $_settings.dataFolder)) {

View File

@@ -0,0 +1,20 @@
Level1;Level2;Level3;Level4
Administration;;;
Administration;Comptabilite;;
Administration;Comptabilite;Factures;
Administration;Comptabilite;Bilans;
Administration;Ressources Humaines;;
Administration;Ressources Humaines;Contrats;
Administration;Ressources Humaines;Fiches de paie;
Projets;;;
Projets;Projet Alpha;;
Projets;Projet Alpha;Documents;
Projets;Projet Alpha;Livrables;
Projets;Projet Beta;;
Projets;Projet Beta;Documents;
Communication;;;
Communication;Interne;;
Communication;Interne;Notes de service;
Communication;Externe;;
Communication;Externe;Communiques de presse;
Communication;Externe;Newsletter;
1 Level1 Level2 Level3 Level4
2 Administration
3 Administration Comptabilite
4 Administration Comptabilite Factures
5 Administration Comptabilite Bilans
6 Administration Ressources Humaines
7 Administration Ressources Humaines Contrats
8 Administration Ressources Humaines Fiches de paie
9 Projets
10 Projets Projet Alpha
11 Projets Projet Alpha Documents
12 Projets Projet Alpha Livrables
13 Projets Projet Beta
14 Projets Projet Beta Documents
15 Communication
16 Communication Interne
17 Communication Interne Notes de service
18 Communication Externe
19 Communication Externe Communiques de presse
20 Communication Externe Newsletter

View File

@@ -137,5 +137,33 @@
"bulk.status.pending": "En attente", "bulk.status.pending": "En attente",
"bulk.status.creating": "Création...", "bulk.status.creating": "Création...",
"bulk.status.ok": "OK", "bulk.status.ok": "OK",
"bulk.status.error": "Erreur" "bulk.status.error": "Erreur",
"tab.structure": " Structure ",
"grp.struct.csv": "Import CSV",
"lbl.struct.desc": "Importez un CSV pour créer une arborescence. Chaque colonne représente un niveau de profondeur.",
"btn.struct.csv": "Charger CSV...",
"grp.struct.preview": "Aperçu",
"grp.struct.target": "Cible",
"lbl.struct.library": "Bibliothèque cible :",
"ph.struct.library": "Documents partagés",
"btn.struct.create": "Créer l'arborescence",
"btn.struct.clear": "Effacer",
"struct.col.path": "Chemin complet",
"struct.col.depth": "Profondeur",
"tab.versions": " Versions ",
"grp.ver.keep": "Versions à conserver",
"lbl.ver.count": "Nombre de versions à garder :",
"chk.ver.date": "Filtrer aussi par date",
"rad.ver.before": "Garder les versions avant le :",
"rad.ver.after": "Garder les versions après le :",
"grp.ver.scope": "Périmètre",
"lbl.ver.library": "Bibliothèque / Dossier :",
"ph.ver.library": "Documents partagés",
"chk.ver.recursive": "Inclure les sous-dossiers (récursif)",
"chk.ver.subsites": "Inclure les sous-sites",
"chk.ver.dryrun": "Simulation (aperçu uniquement, aucune suppression)",
"btn.ver.run": "Nettoyer les versions",
"btn.ver.open": "Ouvrir le rapport"
} }