Compare commits
6 Commits
v1.0.1
...
db0f87dc00
| Author | SHA1 | Date | |
|---|---|---|---|
| db0f87dc00 | |||
| 28e4c21e80 | |||
| 5c5e4b1415 | |||
| 086804edf9 | |||
| 9dc85c8057 | |||
| 9bcbad5d5b |
+611
-56
@@ -1482,6 +1482,10 @@ h1{font-size:21px;font-weight:600;margin-bottom:6px}
|
||||
.hdr{background:#0078d4;color:#fff;padding:22px 28px;border-radius:10px;margin-bottom:22px}
|
||||
.hdr .sub{font-size:13px;opacity:.85;margin-top:4px}
|
||||
.hdr a{color:#cce4ff}
|
||||
.srch{background:#fff;border-radius:8px;padding:10px 14px;margin-bottom:14px;box-shadow:0 1px 4px rgba(0,0,0,.08)}
|
||||
.srch input{width:100%;padding:6px 10px;border:1px solid #ccc;border-radius:4px;font-size:13px;outline:none}
|
||||
.srch input:focus{border-color:#0078d4}
|
||||
.hidden{display:none}
|
||||
.cards{display:flex;gap:14px;margin-bottom:22px}
|
||||
.card{background:#fff;border-radius:8px;padding:16px 20px;flex:1;box-shadow:0 1px 4px rgba(0,0,0,.08);text-align:center}
|
||||
.card .v{font-size:26px;font-weight:700;color:#0078d4}
|
||||
@@ -1534,9 +1538,11 @@ a:hover{text-decoration:underline}
|
||||
<div class="card"><div class="v">$uniqueCount</div><div class="l">Unique Permission Sets</div></div>
|
||||
<div class="card"><div class="v">$userCount</div><div class="l">Distinct Users / Groups</div></div>
|
||||
</div>
|
||||
<div class="wrap"><table>
|
||||
<div class="wrap">
|
||||
<div class="srch"><input type="text" id="q" placeholder="Filter results..." onkeyup="filterTable()"></div>
|
||||
<table>
|
||||
<thead><tr><th>Type</th><th>Name</th><th>Users / Members</th><th>Permission Level</th><th>Granted Through</th><th>Unique Permissions</th></tr></thead>
|
||||
<tbody>
|
||||
<tbody id="tbody">
|
||||
$rows
|
||||
</tbody></table></div>
|
||||
<div class="foot">Generated by SharePoint Toolbox</div>
|
||||
@@ -1595,6 +1601,12 @@ function fallbackCopy(text) {
|
||||
try { document.execCommand('copy'); } catch(e) {}
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
function filterTable(){
|
||||
var q=document.getElementById('q').value.toLowerCase();
|
||||
Array.from(document.getElementById('tbody').rows).forEach(function(r){
|
||||
r.classList.toggle('hidden', q && !r.innerText.toLowerCase().includes(q));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body></html>
|
||||
"@
|
||||
@@ -1720,6 +1732,10 @@ a:hover{text-decoration:underline}
|
||||
.sf-tbl tr:hover td{background:#eaf7ea}
|
||||
.sf-tbl a{color:#2e7d32}
|
||||
.foot{margin-top:18px;text-align:center;font-size:12px;color:#bbb}
|
||||
.srch{background:#fff;border-radius:8px;padding:10px 14px;margin-bottom:14px;box-shadow:0 1px 4px rgba(0,0,0,.08)}
|
||||
.srch input{width:100%;padding:6px 10px;border:1px solid #ccc;border-radius:4px;font-size:13px;outline:none}
|
||||
.srch input:focus{border-color:#107c10}
|
||||
.hidden{display:none}
|
||||
</style>
|
||||
<script>
|
||||
function toggle(i){
|
||||
@@ -1741,12 +1757,22 @@ function toggle(i){
|
||||
<div class="card"><div class="v">$totalFiles</div><div class="l">Total Files</div></div>
|
||||
<div class="card"><div class="v">$libCount</div><div class="l">Libraries / Sites Scanned</div></div>
|
||||
</div>
|
||||
<div class="wrap"><table>
|
||||
<div class="wrap">
|
||||
<div class="srch"><input type="text" id="q" placeholder="Filter results..." onkeyup="filterTable()"></div>
|
||||
<table>
|
||||
<thead><tr><th>Library</th><th>Site</th><th style="text-align:right">Files</th><th style="text-align:right">Size</th><th style="text-align:right">Versions</th><th>Share of Total</th><th style="text-align:right">Last Modified</th></tr></thead>
|
||||
<tbody>
|
||||
<tbody id="tbody">
|
||||
$rows
|
||||
</tbody></table></div>
|
||||
<div class="foot">Generated by SharePoint Toolbox</div>
|
||||
<script>
|
||||
function filterTable(){
|
||||
var q=document.getElementById('q').value.toLowerCase();
|
||||
Array.from(document.getElementById('tbody').rows).forEach(function(r){
|
||||
r.classList.toggle('hidden', q && !r.innerText.toLowerCase().includes(q));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body></html>
|
||||
"@
|
||||
$html | Out-File -FilePath $OutputPath -Encoding UTF8
|
||||
@@ -2828,6 +2854,32 @@ $script:LangDefault = @{
|
||||
"bulk.status.creating" = "Creating..."
|
||||
"bulk.status.ok" = "OK"
|
||||
"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
|
||||
@@ -3580,7 +3632,137 @@ $btnBulkCreate.Size = New-Object System.Drawing.Size(200, 34)
|
||||
|
||||
$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 ───────────────────────────────────────────────────────────────
|
||||
$progressBar = New-Object System.Windows.Forms.ProgressBar
|
||||
@@ -3767,6 +3949,24 @@ $_reg = {
|
||||
& $_reg $script:i18nMap $btnBulkRemove "btn.bulk.remove"
|
||||
& $_reg $script:i18nMap $btnBulkClear "btn.bulk.clear"
|
||||
& $_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
|
||||
& $_reg $script:i18nTabs $tabPerms "tab.perms"
|
||||
@@ -3776,6 +3976,8 @@ $_reg = {
|
||||
& $_reg $script:i18nTabs $tabDupes "tab.dupes"
|
||||
& $_reg $script:i18nTabs $tabTransfer "tab.transfer"
|
||||
& $_reg $script:i18nTabs $tabBulk "tab.bulk"
|
||||
& $_reg $script:i18nTabs $tabStruct "tab.structure"
|
||||
& $_reg $script:i18nTabs $tabVersions "tab.versions"
|
||||
|
||||
# Menu items
|
||||
& $_reg $script:i18nMenus $menuSettings "menu.settings"
|
||||
@@ -3790,10 +3992,12 @@ $script:i18nPlaceholders = [System.Collections.Generic.Dictionary[string,object]
|
||||
& $_reg $script:i18nPlaceholders $txtSrchModBy "ph.modified.by"
|
||||
& $_reg $script:i18nPlaceholders $txtSrchLib "ph.library"
|
||||
& $_reg $script:i18nPlaceholders $txtDupLib "ph.dup.lib"
|
||||
& $_reg $script:i18nPlaceholders $txtStructLib "ph.struct.library"
|
||||
& $_reg $script:i18nPlaceholders $txtXferSrcSite "ph.xfer.site"
|
||||
& $_reg $script:i18nPlaceholders $txtXferSrcLib "ph.xfer.library"
|
||||
& $_reg $script:i18nPlaceholders $txtXferDstSite "ph.xfer.site"
|
||||
& $_reg $script:i18nPlaceholders $txtXferDstLib "ph.xfer.library"
|
||||
& $_reg $script:i18nPlaceholders $txtVerLib "ph.ver.library"
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -5203,30 +5407,41 @@ $btnBulkCsv.Add_Click({
|
||||
$ofd = New-Object System.Windows.Forms.OpenFileDialog
|
||||
$ofd.Filter = "CSV (*.csv)|*.csv|All (*.*)|*.*"
|
||||
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
|
||||
foreach ($r in $rows) {
|
||||
# Accepted column names (case-insensitive via PSObject)
|
||||
$name = if ($r.Name) { $r.Name } elseif ($r.name) { $r.name }
|
||||
elseif ($r.Title) { $r.Title } elseif ($r.title) { $r.title } else { "" }
|
||||
$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 { "" }
|
||||
# Read columns via PSObject properties (case-insensitive)
|
||||
$props = @{}
|
||||
foreach ($p in $r.PSObject.Properties) { $props[$p.Name.ToLower()] = "$($p.Value)".Trim() }
|
||||
|
||||
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
|
||||
if ($type -match '^[Cc]omm') { $type = "Communication" } else { $type = "Team" }
|
||||
Add-BulkListItem @{
|
||||
Name = $name.Trim()
|
||||
Alias = $alias.Trim()
|
||||
Name = $name
|
||||
Alias = $alias
|
||||
Type = $type
|
||||
Template = $tpl.Trim()
|
||||
Owners = $own.Trim()
|
||||
Members = $mem.Trim()
|
||||
Template = $tpl
|
||||
Owners = $own
|
||||
Members = $mem
|
||||
}
|
||||
$count++
|
||||
}
|
||||
@@ -5314,32 +5529,83 @@ $btnBulkCreate.Add_Click({
|
||||
$name = $entry.Name
|
||||
$alias = $entry.Alias
|
||||
$isTeam = $entry.Type -ne "Communication"
|
||||
$owners = @($entry.Owners -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
$members = @($entry.Members -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
$ownerRaw = "$($entry.Owners)"
|
||||
$memberRaw = "$($entry.Members)"
|
||||
$owners = [string[]]@($ownerRaw -split '[,;\s]+' | Where-Object { $_ -match '\S' })
|
||||
$members = [string[]]@($memberRaw -split '[,;\s]+' | Where-Object { $_ -match '\S' })
|
||||
$tplName = $entry.Template
|
||||
|
||||
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
|
||||
$Sync.Queue.Enqueue(@{ Text = "##STATUS##"; Index = ($idx - 1); Value = "Creating..." })
|
||||
|
||||
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
|
||||
$newUrl = if ($isTeam) {
|
||||
if ($owners.Count -gt 0) {
|
||||
New-PnPSite -Type TeamSite -Title $name -Alias $alias -Owners $owners -Wait
|
||||
} else {
|
||||
New-PnPSite -Type TeamSite -Title $name -Alias $alias -Wait
|
||||
}
|
||||
if ($isTeam) {
|
||||
BgLog " Creating TeamSite '$alias' (owners/members added after)..." "DarkGray"
|
||||
$newUrl = New-PnPSite -Type TeamSite -Title $name -Alias $alias -Wait
|
||||
} 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"
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
if ($tplName -and $Params.Templates.ContainsKey($tplName)) {
|
||||
$tpl = $Params.Templates[$tplName]
|
||||
@@ -5380,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.CreatedSites.Add([PSCustomObject]@{
|
||||
@@ -5513,6 +5756,318 @@ $btnBulkCreate.Add_Click({
|
||||
|
||||
#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 ───────────────────────────────
|
||||
$_settings = Load-Settings
|
||||
$script:DataFolder = if ($_settings.dataFolder -and (Test-Path $_settings.dataFolder)) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# Features à ajouter :
|
||||
- Sauvegarde du contexte d'authentification en plus des profils
|
||||
- Possibilité de demander la liste de site auquels un user precis a acces
|
||||
- Copie de site à site
|
||||
- Barre de recherche dans les fichiers HTML exportés
|
||||
@@ -0,0 +1,53 @@
|
||||
"Site","Library","File","TotalVer","Deleted","Kept"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Reporting mboK2/MK2 Analyse.xlsx","110","110","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Reporting mboK2/MK2 Capital account.xlsm","32","32","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Reporting mboK2/MK2 Exit.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Reporting mboK2/MK2 Flux.xlsx","40","40","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Reporting mboK2/MK2 Portfolio.xlsx","25","25","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/BACK OFFICE INTERNE/05 Communication LPs/2022 Cybersécurité/Mail d'accompagnement.docx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/BACK OFFICE INTERNE/06 Contrôle interne/Suivi des documents annuels/Années antérieures/Suivi des documents annuels.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/BACK OFFICE INTERNE/06 Contrôle interne/Transactions personnelles/Années précédentes/Titres interdits 2022-07-26.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/BACK OFFICE INTERNE/06 Contrôle interne/Transactions personnelles/Années précédentes/Titres interdits 2022-09-30.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Juridique/Closings/MBO eng. Closing 3.XLS","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Souscripteurs/FICHIER GLOBAL adresses souscripteurs mbok1.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Souscripteurs/MBOK1 Statistiques investisseurs.xlsx","2","2","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/01. Appels de fonds/MBO K2 Objet des appels.xls","2","2","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Souscripteurs/Fichier de suivi du risque souscripteurs K2.xlsx","4","4","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Souscripteurs/FICHIER GLOBAL Adresse Souscripteurs MBO K2.xlsx","94","94","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Souscripteurs/MBOK2 Stat Investisseurs.xlsx","13","13","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022 05 05 Procédure non flux transfert de parts MBOK2 Stichting.docx","2","2","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 05 13 Procédure Flux Facture fonds BAKERTILLY.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 05 13 Procédure Flux Facture fonds rf AMF.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 05 13 Procédure Flux Facture fonds rf GRANT THORNTON K-1.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 06 15 Procédure Flux Facture fonds CVS SIRA.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 06 15 Procédure Flux Facture fonds DELOITTE.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 06 15 Procédure Flux Facture fonds rf INTRALINKS.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 06 30 Procédure envoi reporting par Intralinks.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 09 15 Procédure Flux Facture fonds payée BAKERTILLY.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 09 15 Procédure Flux Facture fonds payée rf INTRALINKS.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 09 28 Procédure Flux Facture fonds CVS.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 09 28 Procédure Flux Facture fonds rf CLADAG.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 09 28 Procédure Flux Facture fonds rf IM.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK2/Procédures terminées K2/2022/2022 10 18 Procédure Flux Facture fonds CORNET VINCENT SEGUREL.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mbok4/Juridique/04_Bulletins de souscription/97700489_4(MBO Capital 4 - Questionnaire Anti-blanchiment - Levée de fonds).DOC","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mbok4/Juridique/04_Bulletins de souscription/MFN/MK4 - Side-letters.xlsx","3","3","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mbok4/Reporting (clotures aussi)/Old/MBO Capital 4 Capital account 310323.xlsm","9","9","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mbok4/Reporting (clotures aussi)/MBOK4 Administration.xlsx","107","107","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mbok4/Reporting (clotures aussi)/MBOK4 Coupons.xlsx","125","125","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mbok4/Reporting (clotures aussi)/MBOK4 Exit value NEW.xlsx","35","35","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mbok4/Reporting (clotures aussi)/MBOK4 Flux NEW.xlsx","500","500","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mbok4/08. Souscripteurs/MBO K4 fichier global.xlsx","156","156","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Appels de fonds/1 Appel1 8 jan 2003/Appel1 tableau.XLS","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Appels de fonds/17 Appel 17 080211/Appel17 en 2012 XL2003 après X Adveq du 300919.xls","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Appels de fonds/17 Appel 17 080211/Appel17 en 2012 XL2003 après X Morgan Stanley et Hollyport.xls","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Appels de fonds/3 Appel3 12 juin 2003/Appel3 12 juin 2003 Finances.xls","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Appels de fonds/3 Appel3 12 juin 2003/MBO Capital Appel 3 12 juin 2003.xls","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Distributions/Distribution 41 du 9 décembre 2019/Distribution 41 du 9 decembre 2019 pour reporting.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/EUROVL/69b Arrêté 311219/Analyse MK1 31 12 19.xlsx","2","2","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Juridique/Liquidation MBO Capital/MBO CAPITAL_300919_Détail comptes de tiers 30 09 2019.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Juridique/Liquidation MBO Capital/Protocole de transfert d'EHB EASI oct 2019.docx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Juridique/Liquidation MBO Capital/Recap frais liquidation nov 2019.xlsx","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Juridique/Transferts et cessions/MBOK1 Recap transferts engagements.xlsx","14","14","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Reporting MBO K1/18 Rapport 310307/mboK1 stat par type.xls","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Reporting MBO K1/41 Rapport 311212/stat JDM 14 01 2013.xls","1","1","0"
|
||||
"https://mbopartenaire.sharepoint.com/sites/BackOffice_SP","Documents","/sites/BackOffice_SP/Documents partages/FINANCES/FPCI mboK1/Reporting MBO K1/68 Rapport 300919/MBO Capital FPCI Reporting 30 09 2019.docx","1","1","0"
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
+29
-1
@@ -137,5 +137,33 @@
|
||||
"bulk.status.pending": "En attente",
|
||||
"bulk.status.creating": "Création...",
|
||||
"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