fix: harden classroom presentations

This commit is contained in:
ekklesia
2026-09-23 00:02:25 -05:00
parent ec1e0d1cd7
commit 6f2d169355
16 changed files with 709 additions and 215 deletions
+26 -12
View File
@@ -5,15 +5,14 @@ underneath it — where the files live, what is known about each one, and
the two things that have to be right whoever is asking: when a download
has really finished, and when a presentation has really been presented.
WHY THERE IS A MANAGED COPY AT ALL.
WHY THE ORIGINAL LIVES IN THE MANAGED LIBRARY.
A teacher downloads a session through ordinary Chrome, into ordinary
~/Downloads. That file stays exactly where Chrome put it and is never
touched again — not renamed, not moved, not deleted, not by the library
and not by the cleanup. The library keeps its OWN copy, and everything
after the import happens to that copy. So a teacher who tidies their
downloads cannot break a presentation, and a cleanup that removes a
presentation cannot take a teacher's file with it.
A teacher downloads a session into ordinary ~/Downloads. Once the download
is complete, it is moved into this managed library. These locked-down
machines have no general file manager, so leaving another copy in Downloads
only wastes space and makes Remove misleading. The moved original is kept
beside the generated PDF and page images, which means conversion failure is
recoverable and cleanup has one contained directory to remove.
WHY EVERY FORMAT BECOMES A PDF.
@@ -459,6 +458,21 @@ def library(root=None):
return found
def remove(presentation):
"""Delete exactly one managed entry, unless it is on screen now."""
try:
showing = presenting_lock().read_text().strip()
except OSError:
showing = ""
if presentation.id == showing:
return False
shutil.rmtree(presentation.directory, ignore_errors=True)
return not presentation.directory.exists()
def titles_in_use(root=None, exclude=None):
"""Every title already on a card, optionally ignoring one presentation.
@@ -601,10 +615,10 @@ def ledger(root=None):
def remember(mark, value, root=None):
"""Record that this download has been dealt with.
Kept even after a teacher removes the presentation, so that Remove
means removed rather than removed-until-the-next-scan. The original
download is still in Downloads — untouched, as always — and without
this the importer would put it straight back.
The normal successful import moves the source out of Downloads, so it
cannot be found by a later scan. The ledger is still the transaction
record that makes an interrupted same- or cross-filesystem move safe:
startup can tell which half-built directory belongs to which source.
"""
data = ledger(root)
data[mark] = value