Files
Classroom_Desktop/scripts/classroom-desktop-reset
ekklesiaandClaude Opus 5 7f4a122900 feat: one setting per application, and never the shape of the machine
A laptop is going in for the women's Bible study, and the obvious way to
build that — a laptop profile that turns off the children's church
applications — is the wrong way. A laptop may well take children's church,
and a tower may well be a Bible study. Hardware cannot be allowed to
decide purpose, so every optional application gets a setting of its own:

    WONDERINK_ENABLED       already existed; behaviour unchanged
    DVD_ENABLED             new
    PARENT_CALL_ENABLED     new

and DEVICE_TYPE says only what the hardware IS. classroom-desktop-layout
never reads it, and there is a test that says so, because this is the rule
that will be under pressure the first time somebody adds a device.

All three default to true, so an existing classroom that has never heard
of any of them comes up exactly as it did before. The settings are
appended to device.conf by the same mechanism every other setting has
used, so an upgrade gains them without losing a byte an administrator
wrote.

The disc player is the one place hardware still votes, and it is a veto:
DVD_ENABLED can take the player away from a machine that has a drive,
nothing can put it on a machine that has none. A button that cannot work
is worse than no button.

The layout's launcher markers are replaced by a feature block of three
plain booleans. The old mechanism commented a line out of an array, which
worked for launchers and could not express Parent Call, because that is a
whole widget rather than one entry. Three booleans express both, and the
launcher list is now built from them in one fixed order, so a disabled
application closes the row up rather than leaving a gap, and the same
enabled set produces the same bar on every machine in the church.

Also here, because it is the bar and not a feature: sortingStrategy=0.
Plasma's default is 1, "manually", and the task manager accepts a
reordering drop only in that mode — MouseHandler.qml tests
tasksModel.sortMode === SortManual before it will move anything. The
classroom never wrote the key, so it had the default, and any teacher
could drag the launchers into a different order. This is the applet's own
lever refusing the drop rather than something of ours putting the bar back
afterwards.

The test now runs the layout the package actually ships, in node, with
Plasma's API stubbed, and asserts the launcher string that comes out.
Asserting against a copy of the file's text in the test was how the old
one worked, and a copy is a thing that drifts.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-20 20:55:09 -05:00

235 lines
9.8 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# Make the desktop come from the package, every time.
#
# Plasma builds the panel from the classroom's layout script whenever it
# starts with no panel configuration, and keeps whatever it built after
# that. That means an update could change the bar in the package and
# never change it in a room — which is exactly what happened with an icon
# that outlived the file it came from.
#
# So the configuration is removed before the shell starts and the bar is
# rebuilt from the layout at every login. The desktop becomes stateless:
# there is nothing in a teacher's home that an update cannot reach, and a
# panel damaged somehow is repaired by the next login rather than by a
# technician.
#
# Nothing is lost by this. The panel is immutable to teachers by Kiosk
# policy, so there is never anything of theirs in that file.
#
# This runs before plasmashell, as a user service ordered Before it.
CLASSROOM_XDG=/etc/ekklesia-classroom/xdg
CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
say() {
echo "classroom-desktop-reset: $1"
}
# THE CASCADE.
#
# Everything below this line is pointless if Plasma is not reading the
# classroom's settings directory at all: without it the room gets stock
# Plasma, no Kiosk restrictions and a lock screen. The session environment
# is set by /etc/xdg/plasma-workspace/env/zz-ekklesia-classroom.sh, which
# is the mechanism Plasma itself provides. This is the assertion, not the
# mechanism: if the variable arrives here wrong, say so out loud and fix it
# for the shell that is about to start rather than build a wrong desktop
# quietly.
case ":${XDG_CONFIG_DIRS:-}:" in
*:"$CLASSROOM_XDG":*)
;;
*)
say "XDG_CONFIG_DIRS did not contain $CLASSROOM_XDG — repairing it"
export XDG_CONFIG_DIRS="$CLASSROOM_XDG:${XDG_CONFIG_DIRS:-/etc/xdg}"
systemctl --user set-environment \
"XDG_CONFIG_DIRS=$XDG_CONFIG_DIRS" 2>/dev/null || true
;;
esac
# FOREIGN APPEARANCE STATE.
#
# A classroom machine that has been through another desktop arrives with a
# profile that answers questions the package means to answer. Two places
# sit ahead of the classroom's own directory in KConfig's cascade:
#
# ~/.config/kdedefaults what the last look and feel applied wrote.
# Plasma also treats it as the record of which
# look and feel is in force: remove it and the
# next session applies ours again from scratch,
# which resets the palette to the package
# default. That is right for a machine arriving
# from another desktop and wrong for a room that
# chose the light theme, so whose it is decides.
#
# ~/.config/kdeglobals the user's own file, which beats every system
# directory. plasma-apply-colorscheme expands a
# whole palette into it, so a light Breeze scheme
# from a previous desktop would keep the room
# light no matter what the package said.
#
# The colour scheme is the one piece of appearance a room is allowed to
# choose, through classroom-theme, so an Ekklesia scheme is kept and
# anything else is removed. This is not a wipe of the home directory: two
# named pieces of appearance state, neither of them a teacher's work.
# The [General] ColorScheme of a KConfig file, or nothing.
scheme_of() {
awk -F= '
/^\[/ { general = ($0 == "[General]"); next }
general && $1 == "ColorScheme" { print $2; exit }
' "$1" 2>/dev/null || true
}
defaults_are_ours=no
if [[ -d "$CONFIG_HOME/kdedefaults" ]]; then
case "$(scheme_of "$CONFIG_HOME/kdedefaults/kdeglobals")" in
Ekklesia*)
defaults_are_ours=yes
;;
*)
rm -rf "$CONFIG_HOME/kdedefaults"
say "removed another desktop's look and feel defaults"
;;
esac
fi
if [[ -f "$CONFIG_HOME/kdeglobals" ]]; then
scheme=$(scheme_of "$CONFIG_HOME/kdeglobals")
case "$scheme" in
Ekklesia*)
say "keeping the room's colour scheme ($scheme)"
;;
"")
# No scheme named. That is what Plasma leaves behind after it
# applies a look and feel — it writes a hash instead — so the
# palette belongs to whichever look and feel last ran, and the
# directory above has just said whose that was.
if [[ "$defaults_are_ours" == yes ]]; then
say "keeping the palette the classroom's look and feel left"
else
rm -f "$CONFIG_HOME/kdeglobals"
say "removed an unnamed palette from another desktop"
fi
;;
*)
rm -f "$CONFIG_HOME/kdeglobals"
say "removed a foreign palette ($scheme); the package supplies one"
;;
esac
fi
# THE KEYS THAT END A SESSION.
#
# Ctrl+Alt+Delete opened Plasma's session screen in a real classroom and a
# teacher hibernated the machine from it. The chord is a KGlobalAccel
# shortcut, and KGlobalAccel is the one part of Plasma the classroom
# cannot reach through KConfig's cascade: kglobalacceld opens
# kglobalshortcutsrc as a SimpleConfig, so it reads this one file in the
# teacher's home and no system directory at all. Measured — with the
# settings in /etc/ekklesia-classroom/xdg, kreadconfig6 reported the chord
# unbound while the shortcut server went on answering Ctrl+Alt+Del.
#
# So the package's copy is put there directly, before the shortcut server
# starts, exactly as the panel is. Nothing of a teacher's is lost: there
# is no settings application in this room, and KGlobalAccel puts every
# shortcut that is not named in the file back from its own defaults when
# each component registers. The entries the package does name are marked
# immutable, which is what stops it putting those back too.
SHORTCUTS=/etc/ekklesia-classroom/kglobalshortcutsrc
if [[ -r "$SHORTCUTS" ]]; then
if cmp -s "$SHORTCUTS" "$CONFIG_HOME/kglobalshortcutsrc"; then
say "session shortcuts already the package's"
else
install -m 600 "$SHORTCUTS" "$CONFIG_HOME/kglobalshortcutsrc"
say "session shortcuts replaced with the package's"
fi
else
say "WARNING: $SHORTCUTS is missing — Ctrl+Alt+Delete may open the"
say " session screen this room is not supposed to have"
fi
# The room's power behaviour is policy, not preference: never sleep, never
# dim, never lock. PowerDevil loads an idle action when it finds a group
# for it, and the merged view of this file is where such a group could
# come from, so the user's copy goes at every login. Nothing of a
# teacher's is in it — there is no settings application in this room.
if [[ -f "$CONFIG_HOME/powermanagementprofilesrc" ]]; then
rm -f "$CONFIG_HOME/powermanagementprofilesrc"
say "cleared the user's power settings; the package supplies them"
fi
# THE DISC PLAYER, AND WHETHER THIS MACHINE HAS ONE.
#
# The panel about to be built comes from the packaged layout, and the
# feature lines in it are written by ekklesia-classroom-layout.service
# before the display manager starts. This cannot write /usr and does not
# try. It checks one direction only: a layout that offers a player on a
# machine with no drive is a teacher clicking a button that can only
# disappoint, and the journal should say so at the moment the panel is
# built rather than after somebody notices.
#
# The other direction is not a fault. A machine with a drive and
# DVD_ENABLED=false is a device that has been told not to offer the
# player, which is a supported thing to want.
LAYOUT=/usr/share/plasma/look-and-feel/org.ekklesia.classroom.desktop/contents/layouts/org.kde.plasma.desktop-layout.js
if [[ -r "$LAYOUT" ]]; then
if compgen -G "/dev/sr[0-9]*" >/dev/null; then
drive=yes
else
drive=no
fi
if grep -qE '^\s*var DVD = true;' "$LAYOUT"; then
offered=yes
else
offered=no
fi
if [[ "$offered" == yes && "$drive" == no ]]; then
say "WARNING: the layout offers the disc player but this machine"
say " has no drive — run classroom-desktop-layout"
fi
fi
CONFIG="$CONFIG_HOME/plasma-org.kde.plasma.desktop-appletsrc"
if [[ -e "$CONFIG" ]]; then
rm -f "$CONFIG"
say "panel will be rebuilt from the layout"
else
say "no panel configuration to clear"
fi
# A shell package the classroom used to select.
#
# One build chose a shell package of its own so that it could replace the
# volume display. With one selected, plasmashell builds no panel at all —
# and this script deletes the panel at every login, so the room came up
# with an empty screen. The package no longer ships it; this removes the
# choice from a profile that was written while it did.
if [[ -f "$CONFIG_HOME/plasmashellrc" ]] \
&& grep -q "^ShellPackage=" "$CONFIG_HOME/plasmashellrc" 2>/dev/null; then
sed -i "/^ShellPackage=/d" "$CONFIG_HOME/plasmashellrc"
say "removed a shell package choice that leaves the room with no panel"
fi
# The display, like the panel, comes from the package rather than from
# whatever the room last negotiated. KWin remembers a resolution and a
# scale per output in this file, and a television that once came up in the
# wrong mode keeps it for good — the scale service applies UI_SCALE again
# a moment after this, so there is nothing here to preserve. Seen in the
# lab: an output that had been through two scale changes stayed at a
# resolution nothing on the machine had asked for.
rm -f "$CONFIG_HOME/kwinoutputconfig.json" 2>/dev/null || true
# The icon cache remembers a name it once failed to find, which outlives
# the package that later supplies it.
rm -f "$CACHE_HOME/icon-cache.kcache" 2>/dev/null || true