v2026.5.46 — Couche données v2, vue « échelle de temps exacte », session fiabilisée, réservations partagées

This commit is contained in:
2026-07-09 16:21:25 +02:00
parent 06c0195130
commit 975a907f2b
15 changed files with 9351 additions and 2410 deletions
+231
View File
@@ -0,0 +1,231 @@
# 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()