6 Commits

Author SHA1 Message Date
10bfe6debc Merge branch 'main' of https://git.azuze.fr/kawa/Sharepoint-Toolbox
All checks were successful
Release zip package / release (push) Successful in 1s
2026-04-01 17:12:30 +02:00
945a4e110d Update TODO.md 2026-04-01 17:12:24 +02:00
109d0d5f1e Update TODO.md 2026-03-27 09:57:13 +01:00
b4f0fecad2 Merge branch 'main' of https://git.azuze.fr/kawa/Sharepoint-Toolbox
All checks were successful
Release zip package / release (push) Successful in 1s
2026-03-27 09:54:11 +01:00
903fa17f8a Updated workflow to include CSV examples folder 2026-03-27 09:54:01 +01:00
693f21915d Updated workflow to include CSV examples folder
All checks were successful
Release zip package / release (push) Successful in 1s
2026-03-17 11:03:23 +01:00
4 changed files with 174 additions and 17 deletions

View File

@@ -24,7 +24,7 @@ jobs:
cd repo cd repo
VERSION="${{ gitea.ref_name }}" VERSION="${{ gitea.ref_name }}"
ZIP="SharePoint_ToolBox_${VERSION}.zip" ZIP="SharePoint_ToolBox_${VERSION}.zip"
zip -r "../${ZIP}" Sharepoint_ToolBox.ps1 lang/ zip -r "../${ZIP}" Sharepoint_ToolBox.ps1 lang/ examples/
echo "ZIP=${ZIP}" >> "$GITHUB_ENV" echo "ZIP=${ZIP}" >> "$GITHUB_ENV"
echo "VERSION=${VERSION}" >> "$GITHUB_ENV" echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
@@ -34,7 +34,7 @@ jobs:
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases" \ "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \ -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "{\"tag_name\":\"${{ env.VERSION }}\",\"name\":\"SharePoint ToolBox ${{ env.VERSION }}\",\"body\":\"### How to use\\n1. Download and extract the archive\\n2. Launch Sharepoint_ToolBox.ps1 with PowerShell\\n\"}" \ -d "{\"tag_name\":\"${{ env.VERSION }}\",\"name\":\"SharePoint ToolBox ${{ env.VERSION }}\",\"body\":\"1. Download and extract the archive\\n2. Launch Sharepoint_ToolBox.ps1 with PowerShell\\n\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['id'])") | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "RELEASE_ID=${RELEASE_ID}" >> "$GITHUB_ENV" echo "RELEASE_ID=${RELEASE_ID}" >> "$GITHUB_ENV"

View File

@@ -29,7 +29,8 @@ function Format-Bytes([long]$b) {
function Validate-Inputs { function Validate-Inputs {
if ([string]::IsNullOrWhiteSpace($script:txtClientId.Text)) { if ([string]::IsNullOrWhiteSpace($script:txtClientId.Text)) {
[System.Windows.Forms.MessageBox]::Show("Please enter a Client ID.", "Missing Field", "OK", "Warning") $msg = T "validate.missing.clientid.hint"
[System.Windows.Forms.MessageBox]::Show($msg, (T "validate.missing.title"), "OK", "Warning")
return $false return $false
} }
$hasSites = ($script:SelectedSites -and $script:SelectedSites.Count -gt 0) $hasSites = ($script:SelectedSites -and $script:SelectedSites.Count -gt 0)
@@ -2885,6 +2886,21 @@ $script:LangDefault = @{
"chk.ver.dryrun" = "Dry run (preview only, no deletion)" "chk.ver.dryrun" = "Dry run (preview only, no deletion)"
"btn.ver.run" = "Clean Versions" "btn.ver.run" = "Clean Versions"
"btn.ver.open" = "Open Report" "btn.ver.open" = "Open Report"
"btn.register.app" = "Register"
"reg.title" = "App Registration"
"reg.offer" = "No Client ID provided. Register a new app on this tenant?"
"reg.confirm" = "Register 'SharePoint Toolbox' app on tenant {0}?"
"reg.in.progress" = "Registering..."
"reg.success" = "App registered successfully!`nClient ID: {0}`nYou can save this profile to reuse it."
"reg.err.tenant" = "Cannot determine tenant from the provided URL."
"reg.err.nocmd" = "PnP.PowerShell module does not support app registration. Please register the app manually in Entra ID."
"reg.err.no.id" = "Registration completed but no Client ID was returned."
"reg.err.failed" = "Registration failed:`n{0}"
"reg.err.no.tenant" = "Please enter a Tenant URL first."
"reg.err.nopwsh" = "PowerShell 7+ (pwsh) is required for app registration but was not found. Install it from https://aka.ms/powershell"
"validate.missing.clientid" = "Please enter a Client ID."
"validate.missing.clientid.hint" = "Please enter a Client ID or use the 'Register' button to create one."
"validate.missing.title" = "Missing Field"
} }
$script:Lang = $null # null = use LangDefault $script:Lang = $null # null = use LangDefault
@@ -3060,9 +3076,18 @@ $btnBrowseSites.Size = New-Object System.Drawing.Size(92, 26)
$lblClientId = (& $lbl (T "client.id") 20 108) $lblClientId = (& $lbl (T "client.id") 20 108)
$txtClientId = New-Object System.Windows.Forms.TextBox $txtClientId = New-Object System.Windows.Forms.TextBox
$txtClientId.Location = New-Object System.Drawing.Point(140, 108) $txtClientId.Location = New-Object System.Drawing.Point(140, 108)
$txtClientId.Size = New-Object System.Drawing.Size(500, 22) $txtClientId.Size = New-Object System.Drawing.Size(400, 22)
$txtClientId.Font = New-Object System.Drawing.Font("Consolas", 9) $txtClientId.Font = New-Object System.Drawing.Font("Consolas", 9)
$btnRegisterApp = New-Object System.Windows.Forms.Button
$btnRegisterApp.Text = T "btn.register.app"
$btnRegisterApp.Location = New-Object System.Drawing.Point(548, 106)
$btnRegisterApp.Size = New-Object System.Drawing.Size(92, 26)
$txtClientId.Add_TextChanged({
$btnRegisterApp.Enabled = [string]::IsNullOrWhiteSpace($txtClientId.Text)
})
$lblSiteURL = (& $lbl (T "site.url") 20 140) $lblSiteURL = (& $lbl (T "site.url") 20 140)
$txtSiteURL = New-Object System.Windows.Forms.TextBox $txtSiteURL = New-Object System.Windows.Forms.TextBox
$txtSiteURL.Location = New-Object System.Drawing.Point(140, 140) $txtSiteURL.Location = New-Object System.Drawing.Point(140, 140)
@@ -3866,7 +3891,7 @@ $form.Controls.AddRange(@(
$lblProfile, $cboProfile, $lblProfile, $cboProfile,
$btnProfileNew, $btnProfileSave, $btnProfileRename, $btnProfileDelete, $btnProfileNew, $btnProfileSave, $btnProfileRename, $btnProfileDelete,
$lblTenantUrl, $txtTenantUrl, $btnBrowseSites, $lblTenantUrl, $txtTenantUrl, $btnBrowseSites,
$lblClientId, $txtClientId, $lblClientId, $txtClientId, $btnRegisterApp,
$lblSiteURL, $txtSiteURL, $lblSiteURL, $txtSiteURL,
$lblOutput, $txtOutput, $btnBrowse, $lblOutput, $txtOutput, $btnBrowse,
$sep, $tabs, $sep, $tabs,
@@ -3891,6 +3916,7 @@ $_reg = {
& $_reg $script:i18nMap $btnProfileRename "btn.rename" & $_reg $script:i18nMap $btnProfileRename "btn.rename"
& $_reg $script:i18nMap $btnProfileDelete "btn.delete" & $_reg $script:i18nMap $btnProfileDelete "btn.delete"
& $_reg $script:i18nMap $btnBrowseSites "btn.view.sites" & $_reg $script:i18nMap $btnBrowseSites "btn.view.sites"
& $_reg $script:i18nMap $btnRegisterApp "btn.register.app"
& $_reg $script:i18nMap $lblTenantUrl "tenant.url" & $_reg $script:i18nMap $lblTenantUrl "tenant.url"
& $_reg $script:i18nMap $lblClientId "client.id" & $_reg $script:i18nMap $lblClientId "client.id"
& $_reg $script:i18nMap $lblSiteURL "site.url" & $_reg $script:i18nMap $lblSiteURL "site.url"
@@ -4156,6 +4182,126 @@ foreach ($mi in @($menuLang.DropDownItems | Where-Object { $_ -is [System.Window
$mi.Add_Click({ Switch-AppLanguage $args[0].Tag }) $mi.Add_Click({ Switch-AppLanguage $args[0].Tag })
} }
$btnRegisterApp.Add_Click({
$tenantUrl = $txtTenantUrl.Text.Trim()
if ([string]::IsNullOrWhiteSpace($tenantUrl)) {
[System.Windows.Forms.MessageBox]::Show(
(T "reg.err.no.tenant"), (T "reg.title"), "OK", "Warning")
return
}
$confirm = [System.Windows.Forms.MessageBox]::Show(
((T "reg.confirm") -f $tenantUrl),
(T "reg.title"), "YesNo", "Question")
if ($confirm -ne "Yes") { return }
# ── Derive tenant identifier ──────────────────────────────────────────────
if ($tenantUrl -match 'https://([^.]+)\.sharepoint\.com') {
$tenantId = "$($Matches[1]).onmicrosoft.com"
} else {
[System.Windows.Forms.MessageBox]::Show(
(T "reg.err.tenant"), (T "reg.title"), "OK", "Error")
return
}
$btnRegisterApp.Enabled = $false
$btnRegisterApp.Text = T "reg.in.progress"
Write-Log "Registering app on $tenantId ..."
# ── Write a temp script and launch a real PowerShell console ──────────────
$resultFile = Join-Path ([System.IO.Path]::GetTempPath()) "SPToolbox_RegResult.json"
if (Test-Path $resultFile) { Remove-Item $resultFile -Force }
$script:_regResultFile = $resultFile
$scriptContent = @"
`$Host.UI.RawUI.WindowTitle = "SharePoint Toolbox - App Registration"
try {
Import-Module PnP.PowerShell -ErrorAction Stop
} catch {
Write-Host "ERROR: PnP.PowerShell module not found." -ForegroundColor Red
Write-Host `$_.Exception.Message -ForegroundColor Red
@{ Error = `$_.Exception.Message } | ConvertTo-Json | Set-Content -Path "$resultFile" -Encoding UTF8
Read-Host "Press Enter to close"
exit
}
Write-Host "Registering app on $tenantId ..." -ForegroundColor Cyan
Write-Host "A browser window will open for authentication." -ForegroundColor Yellow
Write-Host ""
try {
`$result = Register-PnPEntraIDAppForInteractiveLogin ``
-ApplicationName "SharePoint Toolbox" ``
-Tenant "$tenantId"
`$clientId = `$result.'AzureAppId/ClientId'
if (`$clientId) {
Write-Host "Success! Client ID: `$clientId" -ForegroundColor Green
} else {
Write-Host "WARNING: No Client ID returned." -ForegroundColor Yellow
}
@{ ClientId = `$clientId } | ConvertTo-Json | Set-Content -Path "$resultFile" -Encoding UTF8
} catch {
Write-Host "ERROR: `$(`$_.Exception.Message)" -ForegroundColor Red
@{ Error = `$_.Exception.Message } | ConvertTo-Json | Set-Content -Path "$resultFile" -Encoding UTF8
}
Read-Host "Press Enter to close"
"@
$scriptFile = Join-Path ([System.IO.Path]::GetTempPath()) "SPToolbox_RegApp.ps1"
$scriptContent | Set-Content -Path $scriptFile -Encoding UTF8
$pwshPath = Get-Command pwsh -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source
if (-not $pwshPath) {
$btnRegisterApp.Enabled = [string]::IsNullOrWhiteSpace($txtClientId.Text)
$btnRegisterApp.Text = T "btn.register.app"
Write-Log "PowerShell 7+ (pwsh) not found." "Red"
[System.Windows.Forms.MessageBox]::Show(
(T "reg.err.nopwsh"), (T "reg.title"), "OK", "Error")
return
}
Start-Process $pwshPath -ArgumentList "-ExecutionPolicy Bypass -File `"$scriptFile`""
# ── Timer polls for the result file ──────────────────────────────────────
$tmr = New-Object System.Windows.Forms.Timer
$tmr.Interval = 500
$script:_regTimer = $tmr
$tmr.Add_Tick({
if (Test-Path $script:_regResultFile) {
$script:_regTimer.Stop(); $script:_regTimer.Dispose()
$btnRegisterApp.Text = T "btn.register.app"
try {
$res = Get-Content $script:_regResultFile -Raw | ConvertFrom-Json
Remove-Item $script:_regResultFile -Force -ErrorAction SilentlyContinue
} catch {
Write-Log "Failed to read registration result." "Red"
$btnRegisterApp.Enabled = [string]::IsNullOrWhiteSpace($txtClientId.Text)
return
}
if ($res.Error) {
Write-Log "App registration failed: $($res.Error)" "Red"
$btnRegisterApp.Enabled = $true
[System.Windows.Forms.MessageBox]::Show(
((T "reg.err.failed") -f $res.Error),
(T "reg.title"), "OK", "Error")
} elseif ($res.ClientId) {
$script:txtClientId.Text = $res.ClientId
Write-Log "App registered. Client ID: $($res.ClientId)"
[System.Windows.Forms.MessageBox]::Show(
((T "reg.success") -f $res.ClientId),
(T "reg.title"), "OK", "Information")
} else {
Write-Log "Registration returned no Client ID." "Red"
$btnRegisterApp.Enabled = $true
[System.Windows.Forms.MessageBox]::Show(
(T "reg.err.no.id"), (T "reg.title"), "OK", "Error")
}
} else {
$dot = "." * (([System.DateTime]::Now.Second % 4) + 1)
$btnRegisterApp.Text = (T "reg.in.progress") -replace '\.\.\.$', $dot
}
})
$tmr.Start()
})
$btnBrowseSites.Add_Click({ $btnBrowseSites.Add_Click({
$tenantUrl = $txtTenantUrl.Text.Trim() $tenantUrl = $txtTenantUrl.Text.Trim()
$clientId = $txtClientId.Text.Trim() $clientId = $txtClientId.Text.Trim()
@@ -6057,16 +6203,12 @@ $btnVerOpen.Add_Click({
$btnVerRun.Add_Click({ $btnVerRun.Add_Click({
# --- Gather all selected site URLs --- # --- Gather all selected site URLs ---
$siteUrls = @() $siteUrls = if ($script:SelectedSites -and $script:SelectedSites.Count -gt 0) {
if ($script:_CachedSites -and $script:_CachedSites.Count -gt 0) { @($script:SelectedSites)
foreach ($s in $script:_CachedSites) { } else {
if ($s.Checked) { $siteUrls += $s.Url } @($txtSiteURL.Text.Trim())
}
}
if ($siteUrls.Count -eq 0) {
$single = $txtSiteUrl.Text.Trim()
if ($single) { $siteUrls = @($single) }
} }
$siteUrls = @($siteUrls | Where-Object { $_ })
if ($siteUrls.Count -eq 0) { Write-Log "Site URL required." "Red"; return } if ($siteUrls.Count -eq 0) { Write-Log "Site URL required." "Red"; return }
$clientId = $txtClientId.Text.Trim() $clientId = $txtClientId.Text.Trim()

View File

@@ -1,4 +1,3 @@
# Features à ajouter : # Features à ajouter :
- Sauvegarde du contexte d'authentification en plus des profils - Sauvegarde du contexte d'authentification en plus des profils
- Possibilité de demander la liste de site auquels un user precis a acces - Possibilité de demander la liste de site auquels un user precis a acces
- Barre de recherche dans les fichiers HTML exportés

View File

@@ -170,5 +170,21 @@
"chk.ver.subsites": "Inclure les sous-sites", "chk.ver.subsites": "Inclure les sous-sites",
"chk.ver.dryrun": "Simulation (aperçu uniquement, aucune suppression)", "chk.ver.dryrun": "Simulation (aperçu uniquement, aucune suppression)",
"btn.ver.run": "Nettoyer les versions", "btn.ver.run": "Nettoyer les versions",
"btn.ver.open": "Ouvrir le rapport" "btn.ver.open": "Ouvrir le rapport",
"btn.register.app": "Enregistrer",
"reg.title": "Enregistrement de l'application",
"reg.offer": "Aucun Client ID fourni. Enregistrer une nouvelle application sur ce tenant ?",
"reg.confirm": "Enregistrer l'application 'SharePoint Toolbox' sur le tenant {0} ?",
"reg.in.progress": "En cours...",
"reg.success": "Application enregistrée avec succès !\nClient ID : {0}\nVous pouvez sauvegarder ce profil pour le réutiliser.",
"reg.err.tenant": "Impossible de déterminer le tenant à partir de l'URL fournie.",
"reg.err.nocmd": "Le module PnP.PowerShell ne supporte pas l'enregistrement d'applications. Veuillez enregistrer l'application manuellement dans Entra ID.",
"reg.err.no.id": "Enregistrement terminé mais aucun Client ID n'a été retourné.",
"reg.err.failed": "Échec de l'enregistrement :\n{0}",
"reg.err.no.tenant": "Veuillez d'abord saisir une URL de tenant.",
"reg.err.nopwsh": "PowerShell 7+ (pwsh) est requis pour l'enregistrement d'application mais n'a pas été trouvé. Installez-le depuis https://aka.ms/powershell",
"validate.missing.clientid": "Veuillez saisir un Client ID.",
"validate.missing.clientid.hint": "Veuillez saisir un Client ID ou utilisez le bouton 'Enregistrer' pour en créer un.",
"validate.missing.title": "Champ manquant"
} }