Files
shopdb-flask/plugins/printers/client/Apply-ShopdbDefaultPrinter.ps1
cproudlock 8cedf674fb
Some checks failed
CI / naming (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / migrations-mysql (push) Has been cancelled
CI / backend (push) Has been cancelled
Resolve a driver by vendor, and converge a bay's printers from ShopDB
Two rows now cover 41 of 44 printers. printerdrivers could only bind a driver to
ONE modelnumberid, so the HP and Xerox universal drivers - which between them
cover almost the whole floor - would have needed 21 near-duplicate rows pointing
at the same package. That is a table nobody keeps true, and it is why 42 of 44
printers resolved no driver at all.

printerdrivers gains vendorid, and resolution runs most-specific-first: the
printer's model, then its vendor, then the pre-vendorid convention of matching
the vendor word in the driver's name so a site that populated the table before
the column existed does not silently lose every driver on upgrade. A row that
names a vendor is never matched by its text, because a mis-set vendor resolving
to the wrong package is worse than resolving to none.

Six rows now resolve 44 of 44 printers at the reference site, and the DesignJet
correctly takes its own driver over the HP universal one.

Set-ShopdbPrinters.ps1 is the client half: ask for-host, create the queues that
are missing, record the desired default. It NEVER removes a queue - a bad minute
from the API must not take printers away from a working bay - and it never
fetches a driver, because downloading 48 MB while somebody waits to print is the
wrong moment. The common scope stages those.

Apply-ShopdbDefaultPrinter.ps1 applies the default in the USER's context, which
is the only context that can: SYSTEM cannot set a per-user default for somebody
else. It also turns off "Let Windows manage my default printer", without which
Windows silently overwrites the choice the next time anyone prints elsewhere -
a fix that undoes itself within a day.

VALIDATED ON WINDOWS 11 AGAINST A LIVE SHOPDB, not only by tests. Printers were
assigned to a MACHINE; a PC controlling it, holding no rows of its own, created
both queues bound to the right universal drivers, recorded the default and set
it, and a second run changed nothing. The first attempt failed with
"Relationship types are not seeded - run: flask seed reference-data", which is
the deployment trap the plan predicted, caught by an explicit error rather than
silently resolving nothing.
2026-08-19 10:24:53 -04:00

95 lines
3.8 KiB
PowerShell

# Apply-ShopdbDefaultPrinter.ps1
#
# Sets the logged-on user's default printer to the one ShopDB assigned. Runs IN
# THE USER'S CONTEXT, at logon and on a repeat, because a default printer is
# per-user state that SYSTEM cannot set for somebody else.
#
# Set-ShopdbPrinters.ps1 records the desired queue in
# HKLM:\SOFTWARE\GE\ShopDB DefaultPrinter during the enforcement cycle. This
# reads it. Splitting the two is not tidiness: the machine half needs SYSTEM and
# the share, the user half needs a user - no single process has both.
#
# IT ALSO TURNS OFF "Let Windows manage my default printer". Leaving it on means
# Windows silently overwrites the choice the next time somebody prints to another
# queue, and the bay drifts back with nothing in any log to say why.
#
# Converges: when the current default already matches, it does nothing, so a
# repeating trigger costs a registry read. A user who deliberately picks another
# default WILL be corrected on the next run - that is the intent for a shared
# bay. For a PC where that is wrong, schedule it at logon only.
#
# Exits 0 always.
param(
# Override for testing. Normally read from the machine hive.
[string]$PrinterName = ''
)
$ErrorActionPreference = 'Continue'
$logDir = "$env:LOCALAPPDATA\ShopDB"
if (-not (Test-Path $logDir)) {
New-Item -ItemType Directory -Path $logDir -Force -ErrorAction SilentlyContinue | Out-Null
}
$logFile = Join-Path $logDir 'default-printer.log'
function Log([string]$msg) {
$ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
"$ts $msg" | Tee-Object -FilePath $logFile -Append | Out-Null
}
if (-not $PrinterName) {
foreach ($path in @('HKLM:\SOFTWARE\GE\ShopDB', 'HKLM:\SOFTWARE\WOW6432Node\GE\ShopDB')) {
try {
if (Test-Path $path) {
$value = [string](Get-ItemProperty -Path $path -Name DefaultPrinter -ErrorAction Stop).DefaultPrinter
if ($value -and $value.Trim()) { $PrinterName = $value.Trim(); break }
}
} catch {}
}
}
if (-not $PrinterName) {
# No default assigned is a legitimate state - a bay with three printers and
# no favourite - so leave whatever the user has.
Log 'no default assigned in ShopDB; leaving the current one alone'
exit 0
}
# Windows 10+ overrides any default the moment the user prints elsewhere, unless
# this is off. Setting the default without clearing this is a fix that undoes
# itself within a day.
try {
$windowsKey = 'HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows'
$managed = (Get-ItemProperty -Path $windowsKey -Name LegacyDefaultPrinterMode -ErrorAction SilentlyContinue).LegacyDefaultPrinterMode
if ($managed -ne 1) {
Set-ItemProperty -Path $windowsKey -Name LegacyDefaultPrinterMode -Value 1 -Type DWord
Log 'turned off "Let Windows manage my default printer"'
}
} catch {
Log "WARN could not turn off Windows-managed defaults: $($_.Exception.Message)"
}
$queue = Get-Printer -Name $PrinterName -ErrorAction SilentlyContinue
if (-not $queue) {
# The enforcement cycle creates queues; this runs at logon and may simply be
# earlier than the first cycle on a new bay. Next run picks it up.
Log "assigned default '$PrinterName' is not installed yet; nothing to do"
exit 0
}
$current = (Get-CimInstance -ClassName Win32_Printer -Filter 'Default = True' -ErrorAction SilentlyContinue).Name
if ($current -eq $PrinterName) {
Log "already default: $PrinterName"
exit 0
}
try {
$target = Get-CimInstance -ClassName Win32_Printer -Filter ("Name = '{0}'" -f $PrinterName.Replace("'", "''")) -ErrorAction Stop
Invoke-CimMethod -InputObject $target -MethodName SetDefaultPrinter -ErrorAction Stop | Out-Null
Log "default set: $PrinterName (was '$current')"
} catch {
Log "ERROR setting the default to '$PrinterName': $($_.Exception.Message)"
}
exit 0