Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a1edea3007 | |||
| db0f87dc00 | |||
| 28e4c21e80 | |||
| 5c5e4b1415 | |||
| 086804edf9 | |||
| 9dc85c8057 |
@@ -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
|
||||||
|
|
||||||
@@ -5229,30 +5407,41 @@ $btnBulkCsv.Add_Click({
|
|||||||
$ofd = New-Object System.Windows.Forms.OpenFileDialog
|
$ofd = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
$ofd.Filter = "CSV (*.csv)|*.csv|All (*.*)|*.*"
|
$ofd.Filter = "CSV (*.csv)|*.csv|All (*.*)|*.*"
|
||||||
if ($ofd.ShowDialog($form) -ne "OK") { return }
|
if ($ofd.ShowDialog($form) -ne "OK") { return }
|
||||||
$rows = Import-Csv $ofd.FileName
|
# Try semicolon first (handles commas inside fields), fall back to comma
|
||||||
|
$content = Get-Content $ofd.FileName -Raw
|
||||||
|
if ($content -match ';') {
|
||||||
|
$rows = Import-Csv $ofd.FileName -Delimiter ';'
|
||||||
|
} else {
|
||||||
|
$rows = Import-Csv $ofd.FileName
|
||||||
|
}
|
||||||
$count = 0
|
$count = 0
|
||||||
foreach ($r in $rows) {
|
foreach ($r in $rows) {
|
||||||
# Accepted column names (case-insensitive via PSObject)
|
# Read columns via PSObject properties (case-insensitive)
|
||||||
$name = if ($r.Name) { $r.Name } elseif ($r.name) { $r.name }
|
$props = @{}
|
||||||
elseif ($r.Title) { $r.Title } elseif ($r.title) { $r.title } else { "" }
|
foreach ($p in $r.PSObject.Properties) { $props[$p.Name.ToLower()] = "$($p.Value)".Trim() }
|
||||||
$alias = if ($r.Alias) { $r.Alias } elseif ($r.alias) { $r.alias }
|
|
||||||
elseif ($r.URL) { $r.URL } elseif ($r.url) { $r.url } else { "" }
|
|
||||||
$type = if ($r.Type) { $r.Type } elseif ($r.type) { $r.type } else { "Team" }
|
|
||||||
$tpl = if ($r.Template) { $r.Template } elseif ($r.template) { $r.template } else { "" }
|
|
||||||
$own = if ($r.Owners) { $r.Owners } elseif ($r.owners) { $r.owners }
|
|
||||||
elseif ($r.Owner) { $r.Owner } elseif ($r.owner) { $r.owner } else { "" }
|
|
||||||
$mem = if ($r.Members) { $r.Members } elseif ($r.members) { $r.members } else { "" }
|
|
||||||
|
|
||||||
if (-not $name -or -not $alias) { continue }
|
$name = if ($props['name']) { $props['name'] } elseif ($props['title']) { $props['title'] } else { "" }
|
||||||
|
$alias = if ($props['alias']) { $props['alias'] } elseif ($props['url']) { $props['url'] } else { "" }
|
||||||
|
$type = if ($props['type']) { $props['type'] } else { "Team" }
|
||||||
|
$tpl = if ($props['template']) { $props['template'] } else { "" }
|
||||||
|
$own = if ($props['owners']) { $props['owners'] } elseif ($props['owner']) { $props['owner'] } else { "" }
|
||||||
|
$mem = if ($props['members']) { $props['members'] } else { "" }
|
||||||
|
|
||||||
|
# Name is required; skip empty rows
|
||||||
|
if (-not $name) { continue }
|
||||||
|
# Auto-generate alias from name if not provided
|
||||||
|
if (-not $alias) {
|
||||||
|
$alias = $name.ToLower() -replace '[^a-z0-9\-]', '-' -replace '-+', '-' -replace '^-|-$', ''
|
||||||
|
}
|
||||||
# Normalize type
|
# Normalize type
|
||||||
if ($type -match '^[Cc]omm') { $type = "Communication" } else { $type = "Team" }
|
if ($type -match '^[Cc]omm') { $type = "Communication" } else { $type = "Team" }
|
||||||
Add-BulkListItem @{
|
Add-BulkListItem @{
|
||||||
Name = $name.Trim()
|
Name = $name
|
||||||
Alias = $alias.Trim()
|
Alias = $alias
|
||||||
Type = $type
|
Type = $type
|
||||||
Template = $tpl.Trim()
|
Template = $tpl
|
||||||
Owners = $own.Trim()
|
Owners = $own
|
||||||
Members = $mem.Trim()
|
Members = $mem
|
||||||
}
|
}
|
||||||
$count++
|
$count++
|
||||||
}
|
}
|
||||||
@@ -5340,32 +5529,83 @@ $btnBulkCreate.Add_Click({
|
|||||||
$name = $entry.Name
|
$name = $entry.Name
|
||||||
$alias = $entry.Alias
|
$alias = $entry.Alias
|
||||||
$isTeam = $entry.Type -ne "Communication"
|
$isTeam = $entry.Type -ne "Communication"
|
||||||
$owners = @($entry.Owners -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
$ownerRaw = "$($entry.Owners)"
|
||||||
$members = @($entry.Members -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
$memberRaw = "$($entry.Members)"
|
||||||
|
$owners = [string[]]@($ownerRaw -split '[,;\s]+' | Where-Object { $_ -match '\S' })
|
||||||
|
$members = [string[]]@($memberRaw -split '[,;\s]+' | Where-Object { $_ -match '\S' })
|
||||||
$tplName = $entry.Template
|
$tplName = $entry.Template
|
||||||
|
|
||||||
BgLog "[$idx/$total] Creating '$name' (alias: $alias, type: $($entry.Type))..." "White"
|
BgLog "[$idx/$total] Creating '$name' (alias: $alias, type: $($entry.Type))..." "White"
|
||||||
|
BgLog " DEBUG owners raw='$ownerRaw' parsed=[$($owners -join '|')] count=$($owners.Count)" "Gray"
|
||||||
|
BgLog " DEBUG members raw='$memberRaw' parsed=[$($members -join '|')] count=$($members.Count)" "Gray"
|
||||||
|
|
||||||
|
# TeamSite requires at least one owner
|
||||||
|
if ($isTeam -and $owners.Count -eq 0) {
|
||||||
|
BgLog " ERREUR : TeamSite requires at least one owner — skipping '$name'" "Red"
|
||||||
|
$Sync.Queue.Enqueue(@{ Text = "##STATUS##"; Index = ($idx - 1); Value = "Error: no owner" })
|
||||||
|
$Sync.ErrCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
# Update status
|
# Update status
|
||||||
$Sync.Queue.Enqueue(@{ Text = "##STATUS##"; Index = ($idx - 1); Value = "Creating..." })
|
$Sync.Queue.Enqueue(@{ Text = "##STATUS##"; Index = ($idx - 1); Value = "Creating..." })
|
||||||
|
|
||||||
try {
|
try {
|
||||||
# Create the site
|
# Create the site WITHOUT owners/members (PnP bug: odata.bind empty array)
|
||||||
|
# Current user becomes default owner; we add owners/members after creation
|
||||||
Connect-PnPOnline -Url $adminUrl -Interactive -ClientId $Params.ClientId
|
Connect-PnPOnline -Url $adminUrl -Interactive -ClientId $Params.ClientId
|
||||||
$newUrl = if ($isTeam) {
|
if ($isTeam) {
|
||||||
if ($owners.Count -gt 0) {
|
BgLog " Creating TeamSite '$alias' (owners/members added after)..." "DarkGray"
|
||||||
New-PnPSite -Type TeamSite -Title $name -Alias $alias -Owners $owners -Wait
|
$newUrl = New-PnPSite -Type TeamSite -Title $name -Alias $alias -Wait
|
||||||
} else {
|
|
||||||
New-PnPSite -Type TeamSite -Title $name -Alias $alias -Wait
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
New-PnPSite -Type CommunicationSite -Title $name -Url "$base/sites/$alias" -Wait
|
BgLog " Creating CommunicationSite '$alias'..." "DarkGray"
|
||||||
|
$newUrl = New-PnPSite -Type CommunicationSite -Title $name -Url "$base/sites/$alias" -Wait
|
||||||
}
|
}
|
||||||
BgLog " Site cree : $newUrl" "LightGreen"
|
BgLog " Site cree : $newUrl" "LightGreen"
|
||||||
|
|
||||||
# Connect to the new site for template + members
|
# Connect to the new site for owners/members/template
|
||||||
Connect-PnPOnline -Url $newUrl -Interactive -ClientId $Params.ClientId
|
Connect-PnPOnline -Url $newUrl -Interactive -ClientId $Params.ClientId
|
||||||
|
|
||||||
|
# Assign owners & members post-creation
|
||||||
|
if ($isTeam) {
|
||||||
|
$groupId = $null
|
||||||
|
try { $groupId = (Get-PnPSite -Includes GroupId).GroupId.Guid } catch {}
|
||||||
|
if ($groupId) {
|
||||||
|
foreach ($o in $owners) {
|
||||||
|
try {
|
||||||
|
Add-PnPMicrosoft365GroupOwner -Identity $groupId -Users $o -ErrorAction Stop
|
||||||
|
BgLog " Owner added: $o" "Cyan"
|
||||||
|
} catch { BgLog " Warn owner '$o': $($_.Exception.Message)" "DarkYellow" }
|
||||||
|
}
|
||||||
|
foreach ($m in $members) {
|
||||||
|
try {
|
||||||
|
Add-PnPMicrosoft365GroupMember -Identity $groupId -Users $m -ErrorAction Stop
|
||||||
|
BgLog " Member added: $m" "Cyan"
|
||||||
|
} catch { BgLog " Warn member '$m': $($_.Exception.Message)" "DarkYellow" }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BgLog " Could not get M365 GroupId — owners/members not assigned" "DarkYellow"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# CommunicationSite — classic SharePoint groups
|
||||||
|
if ($owners.Count -gt 0) {
|
||||||
|
$ownerGrp = Get-PnPGroup | Where-Object { $_.Title -like "*Propri*" -or $_.Title -like "*Owner*" } | Select-Object -First 1
|
||||||
|
if ($ownerGrp) {
|
||||||
|
foreach ($o in $owners) {
|
||||||
|
try { Add-PnPGroupMember -LoginName $o -Group $ownerGrp.Title -ErrorAction SilentlyContinue } catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($members.Count -gt 0) {
|
||||||
|
$memberGrp = Get-PnPGroup | Where-Object { $_.Title -like "*Membre*" -or $_.Title -like "*Member*" } | Select-Object -First 1
|
||||||
|
if ($memberGrp) {
|
||||||
|
foreach ($m in $members) {
|
||||||
|
try { Add-PnPGroupMember -LoginName $m -Group $memberGrp.Title -ErrorAction SilentlyContinue } catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Apply template if specified
|
# Apply template if specified
|
||||||
if ($tplName -and $Params.Templates.ContainsKey($tplName)) {
|
if ($tplName -and $Params.Templates.ContainsKey($tplName)) {
|
||||||
$tpl = $Params.Templates[$tplName]
|
$tpl = $Params.Templates[$tplName]
|
||||||
@@ -5406,30 +5646,7 @@ $btnBulkCreate.Add_Click({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add members
|
|
||||||
if ($members.Count -gt 0) {
|
|
||||||
$memberGroup = Get-PnPGroup | Where-Object { $_.Title -like "*Membres*" -or $_.Title -like "*Members*" } | Select-Object -First 1
|
|
||||||
if ($memberGroup) {
|
|
||||||
foreach ($m in $members) {
|
|
||||||
try {
|
|
||||||
Add-PnPGroupMember -LoginName $m -Group $memberGroup.Title -ErrorAction SilentlyContinue
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
BgLog " $($members.Count) member(s) added." "Cyan"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add owners for Communication sites (TeamSite owners set at creation)
|
|
||||||
if (-not $isTeam -and $owners.Count -gt 0) {
|
|
||||||
$ownerGroup = Get-PnPGroup | Where-Object { $_.Title -like "*Propri*" -or $_.Title -like "*Owner*" } | Select-Object -First 1
|
|
||||||
if ($ownerGroup) {
|
|
||||||
foreach ($o in $owners) {
|
|
||||||
try {
|
|
||||||
Add-PnPGroupMember -LoginName $o -Group $ownerGroup.Title -ErrorAction SilentlyContinue
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$Sync.Queue.Enqueue(@{ Text = "##STATUS##"; Index = ($idx - 1); Value = "OK" })
|
$Sync.Queue.Enqueue(@{ Text = "##STATUS##"; Index = ($idx - 1); Value = "OK" })
|
||||||
$Sync.CreatedSites.Add([PSCustomObject]@{
|
$Sync.CreatedSites.Add([PSCustomObject]@{
|
||||||
@@ -5539,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)) {
|
||||||
|
|||||||
8
examples/bulk_add_members.csv
Normal file
8
examples/bulk_add_members.csv
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Email
|
||||||
|
user1@contoso.com
|
||||||
|
user2@contoso.com
|
||||||
|
user3@contoso.com
|
||||||
|
manager1@contoso.com
|
||||||
|
designer@contoso.com
|
||||||
|
analyste@contoso.com
|
||||||
|
stagiaire@contoso.com
|
||||||
|
6
examples/bulk_create_sites.csv
Normal file
6
examples/bulk_create_sites.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Name;Alias;Type;Template;Owners;Members
|
||||||
|
Projet Alpha;projet-alpha;Team;;admin@contoso.com;user1@contoso.com, user2@contoso.com
|
||||||
|
Projet Beta;;Team;;admin@contoso.com;user3@contoso.com, user4@contoso.com
|
||||||
|
Communication RH;;Communication;;rh-admin@contoso.com;manager1@contoso.com, manager2@contoso.com
|
||||||
|
Equipe Marketing;equipe-marketing;Team;;marketing-lead@contoso.com;designer@contoso.com, redacteur@contoso.com
|
||||||
|
Portail Intranet;;Communication;;it-admin@contoso.com;
|
||||||
|
20
examples/folder_structure.csv
Normal file
20
examples/folder_structure.csv
Normal 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;
|
||||||
|
30
lang/fr.json
30
lang/fr.json
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user