Added sample CSV files for user/sites importation.
All checks were successful
Release zip package / release (push) Successful in 1s

Fixed a few bugs.
This commit is contained in:
2026-03-16 13:45:00 +01:00
parent 9bcbad5d5b
commit 9dc85c8057
3 changed files with 104 additions and 51 deletions

View File

@@ -5229,30 +5229,41 @@ $btnBulkCsv.Add_Click({
$ofd = New-Object System.Windows.Forms.OpenFileDialog
$ofd.Filter = "CSV (*.csv)|*.csv|All (*.*)|*.*"
if ($ofd.ShowDialog($form) -ne "OK") { return }
# 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++
}
@@ -5340,32 +5351,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
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 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]
@@ -5406,30 +5468,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]@{

View 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
1 Email
2 user1@contoso.com
3 user2@contoso.com
4 user3@contoso.com
5 manager1@contoso.com
6 designer@contoso.com
7 analyste@contoso.com
8 stagiaire@contoso.com

View 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;
1 Name Alias Type Template Owners Members
2 Projet Alpha projet-alpha Team admin@contoso.com user1@contoso.com, user2@contoso.com
3 Projet Beta Team admin@contoso.com user3@contoso.com, user4@contoso.com
4 Communication RH Communication rh-admin@contoso.com manager1@contoso.com, manager2@contoso.com
5 Equipe Marketing equipe-marketing Team marketing-lead@contoso.com designer@contoso.com, redacteur@contoso.com
6 Portail Intranet Communication it-admin@contoso.com