forked from FroSteel/Planification
232 lines
12 KiB
PowerShell
232 lines
12 KiB
PowerShell
# Planification - gestionnaire graphique du helper de sync. // 2026.1.5 - QRO
|
|
# Developpe par Quentin Rouiller (QRO) - DGNSI, Canton de Vaud.
|
|
# Licence MIT - voir le fichier LICENSE.
|
|
#
|
|
# Fenetre avec : Installer / Mettre a jour, Reparer, Desinstaller.
|
|
# Per-user (HKCU), sans admin, sans signature. Lance par Gerer.cmd.
|
|
#
|
|
# Structure du paquet : Gerer.cmd (racine), bin\ (manage/host/tray/.vbs),
|
|
# assets\ (icon.png). Structure installee (%LOCALAPPDATA%\Planification) :
|
|
# bin\ (host.ps1, host.bat, tray.ps1, tray.vbs), assets\ (icon.png),
|
|
# manifests\ (nm-*.json), activity.log (a la racine).
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Add-Type -AssemblyName System.Windows.Forms
|
|
Add-Type -AssemblyName System.Drawing
|
|
|
|
$HostName = 'ch.netaplaid.planification'
|
|
$ExtIdChromium = 'hlkepgcngeckdbapafkheondiagjgphc'
|
|
$ExtIdFirefox = '[email protected]'
|
|
|
|
$ScriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path } # paquet\bin
|
|
$PkgRoot = Split-Path -Parent $ScriptDir # paquet\
|
|
|
|
$InstallDir = Join-Path $env:LOCALAPPDATA 'Planification'
|
|
$InstallBin = Join-Path $InstallDir 'bin'
|
|
$InstallAssets = Join-Path $InstallDir 'assets'
|
|
$InstallManifests = Join-Path $InstallDir 'manifests'
|
|
|
|
function Get-HostVersion([string]$psFile) {
|
|
if (Test-Path -LiteralPath $psFile) {
|
|
$m = Select-String -LiteralPath $psFile -Pattern "HOST_VERSION\s*=\s*'([^']*)'" -ErrorAction SilentlyContinue
|
|
if ($m) { return $m.Matches[0].Groups[1].Value }
|
|
}
|
|
return $null
|
|
}
|
|
|
|
function Stop-Tray {
|
|
Get-CimInstance Win32_Process -Filter "Name='wscript.exe'" -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.CommandLine -like '*tray.vbs*' } |
|
|
ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
|
|
Get-CimInstance Win32_Process -Filter "Name='powershell.exe'" -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.CommandLine -like '*tray.ps1*' } |
|
|
ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
|
|
}
|
|
|
|
function Install-Host {
|
|
Stop-Tray
|
|
foreach ($d in @($InstallBin, $InstallAssets, $InstallManifests)) { New-Item -ItemType Directory -Path $d -Force | Out-Null }
|
|
|
|
# Scripts -> bin\ (ecriture fraiche = pas de Mark of the Web).
|
|
# v2026.1.7 - on copie AUSSI manage.ps1 dans l'install pour que le gestionnaire
|
|
# (Reparer / Desinstaller) reste accessible via le Menu Demarrer meme apres
|
|
# suppression du dossier du paquet.
|
|
foreach ($name in @('host.ps1', 'tray.ps1', 'manage.ps1')) {
|
|
$src = Join-Path $ScriptDir $name
|
|
if (Test-Path -LiteralPath $src) {
|
|
Get-Content -LiteralPath $src -Raw | Set-Content -LiteralPath (Join-Path $InstallBin $name) -Encoding UTF8
|
|
Unblock-File -LiteralPath (Join-Path $InstallBin $name) -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
$srcVbs = Join-Path $ScriptDir 'tray.vbs'
|
|
if (Test-Path -LiteralPath $srcVbs) {
|
|
Get-Content -LiteralPath $srcVbs -Raw | Set-Content -LiteralPath (Join-Path $InstallBin 'tray.vbs') -Encoding ASCII
|
|
Unblock-File -LiteralPath (Join-Path $InstallBin 'tray.vbs') -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
# Icone -> assets\ (binaire : Copy-Item).
|
|
$srcIcon = Join-Path $PkgRoot 'assets\icon.png'
|
|
if (Test-Path -LiteralPath $srcIcon) { Copy-Item -LiteralPath $srcIcon -Destination (Join-Path $InstallAssets 'icon.png') -Force }
|
|
|
|
# Wrapper .bat dans bin\ (host.ps1 est dans le meme dossier -> %~dp0host.ps1).
|
|
$dstBat = Join-Path $InstallBin 'host.bat'
|
|
Set-Content -LiteralPath $dstBat -Encoding ASCII -Value @(
|
|
'@echo off',
|
|
'powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "%~dp0host.ps1" %*'
|
|
)
|
|
|
|
# Manifests natifs -> manifests\ (path = bin\host.bat).
|
|
$manChromium = Join-Path $InstallManifests 'nm-chromium.json'
|
|
$manFirefox = Join-Path $InstallManifests 'nm-firefox.json'
|
|
(([ordered]@{ name=$HostName; description='Planification cache sync host'; path=$dstBat; type='stdio'; allowed_origins=@("chrome-extension://$ExtIdChromium/") }) | ConvertTo-Json -Depth 5) | Set-Content -LiteralPath $manChromium -Encoding UTF8
|
|
(([ordered]@{ name=$HostName; description='Planification cache sync host'; path=$dstBat; type='stdio'; allowed_extensions=@($ExtIdFirefox) }) | ConvertTo-Json -Depth 5) | Set-Content -LiteralPath $manFirefox -Encoding UTF8
|
|
|
|
function Reg([string]$base, [string]$manifest) {
|
|
$key = Join-Path $base $HostName
|
|
New-Item -Path $key -Force | Out-Null
|
|
Set-Item -Path $key -Value $manifest
|
|
}
|
|
Reg 'HKCU:\Software\Google\Chrome\NativeMessagingHosts' $manChromium
|
|
Reg 'HKCU:\Software\Microsoft\Edge\NativeMessagingHosts' $manChromium
|
|
Reg 'HKCU:\Software\Mozilla\NativeMessagingHosts' $manFirefox
|
|
|
|
# v2026.1.7 - le tray ne demarre PLUS au login et n'est PLUS lance a l'install.
|
|
# Il est demarre A LA DEMANDE par host.ps1 quand l'extension se connecte, et
|
|
# s'auto-ferme apres 2 h sans activite. On retire l'ancien autostart s'il
|
|
# existe (installs precedentes) pour que "de base = eteint".
|
|
Remove-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -Name 'PlanificationTray' -ErrorAction SilentlyContinue
|
|
|
|
# v2026.1.7 - Menu Demarrer : raccourci vers le gestionnaire installe
|
|
# (Installer / Reparer / Desinstaller), retrouvable sans le dossier du paquet.
|
|
# 1) lanceur .vbs (fenetre PowerShell masquee) du manage.ps1 installe.
|
|
$dstGererVbs = Join-Path $InstallBin 'gerer.vbs'
|
|
Set-Content -LiteralPath $dstGererVbs -Encoding ASCII -Value @(
|
|
"' Lance le gestionnaire Planification (fenetre PowerShell masquee).",
|
|
'Set sh = CreateObject("WScript.Shell")',
|
|
'Set fso = CreateObject("Scripting.FileSystemObject")',
|
|
'dir = fso.GetParentFolderName(WScript.ScriptFullName)',
|
|
'sh.Run "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File """ & dir & "\manage.ps1""", 0, False'
|
|
)
|
|
# 2) icone .ico (depuis icon.png) pour le raccourci, best-effort.
|
|
$icoPath = Join-Path $InstallAssets 'icon.ico'
|
|
try {
|
|
$pngForIco = Join-Path $InstallAssets 'icon.png'
|
|
if (Test-Path -LiteralPath $pngForIco) {
|
|
Add-Type -AssemblyName System.Drawing
|
|
$bmp = New-Object System.Drawing.Bitmap($pngForIco)
|
|
$ico = [System.Drawing.Icon]::FromHandle($bmp.GetHicon())
|
|
$fs = [System.IO.File]::Create($icoPath)
|
|
$ico.Save($fs); $fs.Close()
|
|
}
|
|
} catch { $icoPath = $null }
|
|
# 3) raccourcis .lnk dans le Menu Demarrer (deux entrees) :
|
|
# - "gerer" -> manage.ps1 (installer / reparer / desinstaller)
|
|
# - "demarrer" -> tray.vbs (relance/affiche la sync a la demande)
|
|
$dstTrayVbs = Join-Path $InstallBin 'tray.vbs'
|
|
try {
|
|
$startDir = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs'
|
|
New-Item -ItemType Directory -Path $startDir -Force | Out-Null
|
|
# Nettoyage de l'ancien nom de raccourci (installs precedentes).
|
|
Remove-Item -LiteralPath (Join-Path $startDir 'Planification - synchronisation.lnk') -Force -ErrorAction SilentlyContinue
|
|
$ws = New-Object -ComObject WScript.Shell
|
|
function New-Lnk([string]$name, [string]$targetVbs, [string]$desc) {
|
|
$sc = $ws.CreateShortcut((Join-Path $startDir $name))
|
|
$sc.TargetPath = 'wscript.exe'
|
|
$sc.Arguments = '"' + $targetVbs + '"'
|
|
$sc.WorkingDirectory = $InstallBin
|
|
$sc.Description = $desc
|
|
if ($icoPath -and (Test-Path -LiteralPath $icoPath)) { $sc.IconLocation = "$icoPath,0" }
|
|
$sc.Save()
|
|
}
|
|
New-Lnk 'Planification - gerer.lnk' $dstGererVbs 'Gerer la synchronisation Planification (installer / reparer / desinstaller)'
|
|
New-Lnk 'Planification - demarrer la synchro.lnk' $dstTrayVbs 'Demarrer / afficher la synchronisation Planification'
|
|
} catch {}
|
|
}
|
|
|
|
function Uninstall-Host {
|
|
Stop-Tray
|
|
Remove-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -Name 'PlanificationTray' -ErrorAction SilentlyContinue
|
|
foreach ($base in @(
|
|
'HKCU:\Software\Google\Chrome\NativeMessagingHosts',
|
|
'HKCU:\Software\Microsoft\Edge\NativeMessagingHosts',
|
|
'HKCU:\Software\Mozilla\NativeMessagingHosts')) {
|
|
Remove-Item -Path (Join-Path $base $HostName) -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
if (Test-Path -LiteralPath $InstallDir) { Remove-Item -LiteralPath $InstallDir -Recurse -Force -ErrorAction SilentlyContinue }
|
|
# v2026.1.7 - retirer les raccourcis du Menu Demarrer (hors InstallDir).
|
|
$startDir = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs'
|
|
foreach ($lnk in @('Planification - synchronisation.lnk', 'Planification - gerer.lnk', 'Planification - demarrer la synchro.lnk')) {
|
|
Remove-Item -LiteralPath (Join-Path $startDir $lnk) -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
# ----------------------------- Interface -----------------------------------
|
|
$available = Get-HostVersion (Join-Path $ScriptDir 'host.ps1')
|
|
|
|
$form = New-Object System.Windows.Forms.Form
|
|
$form.Text = 'Planification - synchronisation'
|
|
$form.Size = New-Object System.Drawing.Size(460, 320)
|
|
$form.StartPosition = 'CenterScreen'
|
|
$form.FormBorderStyle = 'FixedSingle'
|
|
$form.MaximizeBox = $false
|
|
try {
|
|
$ic = Join-Path $PkgRoot 'assets\icon.png'
|
|
if (Test-Path -LiteralPath $ic) { $form.Icon = [System.Drawing.Icon]::FromHandle((New-Object System.Drawing.Bitmap($ic)).GetHicon()) }
|
|
} catch {}
|
|
|
|
$title = New-Object System.Windows.Forms.Label
|
|
$title.Text = 'Synchronisation du planning entre postes'
|
|
$title.Font = New-Object System.Drawing.Font('Segoe UI', 11, [System.Drawing.FontStyle]::Bold)
|
|
$title.AutoSize = $true
|
|
$title.Location = New-Object System.Drawing.Point(18, 16)
|
|
$form.Controls.Add($title)
|
|
|
|
$status = New-Object System.Windows.Forms.Label
|
|
$status.AutoSize = $false
|
|
$status.Size = New-Object System.Drawing.Size(420, 40)
|
|
$status.Location = New-Object System.Drawing.Point(18, 48)
|
|
$form.Controls.Add($status)
|
|
|
|
function Refresh-Status {
|
|
$installed = Get-HostVersion (Join-Path $InstallBin 'host.ps1')
|
|
if ($installed) { $status.Text = "Installe : version $installed`r`nDisponible (ce dossier) : version $available" }
|
|
else { $status.Text = "Non installe sur ce poste.`r`nDisponible (ce dossier) : version $available" }
|
|
}
|
|
Refresh-Status
|
|
|
|
$result = New-Object System.Windows.Forms.Label
|
|
$result.AutoSize = $false
|
|
$result.Size = New-Object System.Drawing.Size(420, 30)
|
|
$result.Location = New-Object System.Drawing.Point(18, 248)
|
|
$result.ForeColor = [System.Drawing.Color]::DarkGreen
|
|
|
|
function Add-Button([string]$text, [int]$y, [scriptblock]$action) {
|
|
$b = New-Object System.Windows.Forms.Button
|
|
$b.Text = $text
|
|
$b.Size = New-Object System.Drawing.Size(420, 38)
|
|
$b.Location = New-Object System.Drawing.Point(18, $y)
|
|
$b.Font = New-Object System.Drawing.Font('Segoe UI', 10)
|
|
$b.add_Click($action)
|
|
$form.Controls.Add($b)
|
|
}
|
|
|
|
Add-Button 'Installer / Mettre a jour' 100 {
|
|
try { Install-Host; $result.ForeColor=[System.Drawing.Color]::DarkGreen; $result.Text='Installation / mise a jour terminee.'; Refresh-Status }
|
|
catch { $result.ForeColor=[System.Drawing.Color]::Firebrick; $result.Text=('Erreur : ' + $_.Exception.Message) }
|
|
}
|
|
Add-Button 'Reparer (reinstaller proprement)' 144 {
|
|
try { Install-Host; $result.ForeColor=[System.Drawing.Color]::DarkGreen; $result.Text='Reparation terminee.'; Refresh-Status }
|
|
catch { $result.ForeColor=[System.Drawing.Color]::Firebrick; $result.Text=('Erreur : ' + $_.Exception.Message) }
|
|
}
|
|
Add-Button 'Desinstaller' 188 {
|
|
$r = [System.Windows.Forms.MessageBox]::Show('Desinstaller le helper de synchronisation ? (le cache du dossier partage n''est pas touche)', 'Confirmer', 'YesNo', 'Question')
|
|
if ($r -eq 'Yes') {
|
|
try { Uninstall-Host; $result.ForeColor=[System.Drawing.Color]::DarkGreen; $result.Text='Desinstalle. Pense a retirer l''extension du navigateur.'; Refresh-Status }
|
|
catch { $result.ForeColor=[System.Drawing.Color]::Firebrick; $result.Text=('Erreur : ' + $_.Exception.Message) }
|
|
}
|
|
}
|
|
|
|
$form.Controls.Add($result)
|
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
|
[void]$form.ShowDialog()
|