diff --git a/Sharepoint_ToolBox.ps1 b/Sharepoint_ToolBox.ps1 index 5e2e9a4..08003d8 100644 --- a/Sharepoint_ToolBox.ps1 +++ b/Sharepoint_ToolBox.ps1 @@ -29,7 +29,8 @@ function Format-Bytes([long]$b) { function Validate-Inputs { 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 } $hasSites = ($script:SelectedSites -and $script:SelectedSites.Count -gt 0) @@ -2885,6 +2886,21 @@ $script:LangDefault = @{ "chk.ver.dryrun" = "Dry run (preview only, no deletion)" "btn.ver.run" = "Clean Versions" "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 @@ -3060,9 +3076,18 @@ $btnBrowseSites.Size = New-Object System.Drawing.Size(92, 26) $lblClientId = (& $lbl (T "client.id") 20 108) $txtClientId = New-Object System.Windows.Forms.TextBox $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) +$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) $txtSiteURL = New-Object System.Windows.Forms.TextBox $txtSiteURL.Location = New-Object System.Drawing.Point(140, 140) @@ -3866,7 +3891,7 @@ $form.Controls.AddRange(@( $lblProfile, $cboProfile, $btnProfileNew, $btnProfileSave, $btnProfileRename, $btnProfileDelete, $lblTenantUrl, $txtTenantUrl, $btnBrowseSites, - $lblClientId, $txtClientId, + $lblClientId, $txtClientId, $btnRegisterApp, $lblSiteURL, $txtSiteURL, $lblOutput, $txtOutput, $btnBrowse, $sep, $tabs, @@ -3891,6 +3916,7 @@ $_reg = { & $_reg $script:i18nMap $btnProfileRename "btn.rename" & $_reg $script:i18nMap $btnProfileDelete "btn.delete" & $_reg $script:i18nMap $btnBrowseSites "btn.view.sites" +& $_reg $script:i18nMap $btnRegisterApp "btn.register.app" & $_reg $script:i18nMap $lblTenantUrl "tenant.url" & $_reg $script:i18nMap $lblClientId "client.id" & $_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 }) } +$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({ $tenantUrl = $txtTenantUrl.Text.Trim() $clientId = $txtClientId.Text.Trim() diff --git a/TODO.md b/TODO.md index 7461565..e079be6 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,3 @@ # Features à ajouter : - Sauvegarde du contexte d'authentification en plus des profils -- Possibilité de demander la liste de site auquels un user precis a acces -- Barre de recherche dans les fichiers HTML exportés \ No newline at end of file +- Possibilité de demander la liste de site auquels un user precis a acces \ No newline at end of file diff --git a/lang/fr.json b/lang/fr.json index 509ae26..76dd2e1 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -170,5 +170,21 @@ "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" + "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" }