From f2942a2946ac1fcb7a6a562df9a9dc50525694f6 Mon Sep 17 00:00:00 2001 From: ekklesia Date: Tue, 22 Sep 2026 20:21:39 -0500 Subject: [PATCH] docs: one manual, the pages a device has and a printable copy of all of them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documentation becomes part of the release rather than a job afterwards. docs/manual is now the only place teacher instructions are written, and tools/build-manual produces both versions from it: classroom-manual.html installed on every machine, showing only the features THAT device has classroom-manual.pdf the universal printed manual, every feature, with a conditional sentence above each optional page so it stays true in a room that does not have them all They cannot drift apart because neither is written by hand, and build-release.sh builds and checks them on every release: a page that declares a feature with no printed-if fails the build, because that is exactly how the printed manual comes to state an optional feature as though every room had it. So does a page naming a feature device.conf does not have, and an optional feature with no page at all. WHAT REACHES THE CLASSROOM ACCOUNT. Only a list of setting NAMES, and only ones the package already defines as optional applications. classroom-desktop-layout — already the one place that decides what a device offers — writes them to /run/ekklesia-classroom/features.json, and classroom-manual filters that against FEATURES before using it. No values from device.conf, no broker password, no addresses. With no manifest at all every page is shown, which is the printed manual and is never wrong, only less specific. Help is a Quick Launch button beside Parent Call, deliberately not in the row with the applications: putting it there would move buttons teachers reach for without looking. Every room gets it, because a teacher who needs the manual is by definition one who does not know where to look. The PDF is written directly rather than converted. A manual is headings, paragraphs, bullets, steps and one table; writing that with the standard fonts is a couple of hundred lines and no dependency, so a release can be built on any machine with Python. Pictures are embedded in the digital manual as data URIs so it stays one file that works wherever it is copied and with the network unplugged; the printed manual names them instead, which is the one thing this builder does not do and is noted where somebody would look for it. Two defects found building it, both of which hung or corrupted the output rather than failing: a paragraph opening with a bold word matched the bullet guard but not the bullet rule, so nothing consumed it and the build spun; and a list item wrapped across two source lines ended the list, so a three-step procedure printed as three steps all numbered 1. Also adds the 0.2.6 physical checklist to docs/testing.md and the release validator's checks for the Wi-Fi regression, the conversion pipeline on a real Debian, the fonts, the manual and the dock order. Co-Authored-By: Claude Opus 5 --- .../applications/ekklesia-manual.desktop | 19 + docs/manual/00-welcome.md | 24 + docs/manual/10-browser.md | 28 + docs/manual/20-sound.md | 27 + docs/manual/40-presentations.md | 118 +++ docs/manual/50-wonderink.md | 35 + docs/manual/60-parent-call.md | 28 + docs/manual/70-dvd.md | 30 + docs/manual/80-printing.md | 23 + docs/manual/90-trouble.md | 35 + docs/manual/README.md | 85 ++ docs/manual/build/classroom-manual.html | 214 +++++ docs/manual/build/classroom-manual.pdf | 362 +++++++++ docs/manual/images/library.png | Bin 0 -> 52358 bytes docs/manual/images/quick-look.png | Bin 0 -> 68219 bytes docs/testing.md | 100 +++ scripts/classroom-manual | 172 ++++ tests/test-classroom-manual.sh | 263 +++++++ tools/build-manual | 736 ++++++++++++++++++ tools/build-release.sh | 62 +- tools/validate-trixie-release.sh | 175 +++++ 21 files changed, 2533 insertions(+), 3 deletions(-) create mode 100644 config/plasma/applications/ekklesia-manual.desktop create mode 100644 docs/manual/00-welcome.md create mode 100644 docs/manual/10-browser.md create mode 100644 docs/manual/20-sound.md create mode 100644 docs/manual/40-presentations.md create mode 100644 docs/manual/50-wonderink.md create mode 100644 docs/manual/60-parent-call.md create mode 100644 docs/manual/70-dvd.md create mode 100644 docs/manual/80-printing.md create mode 100644 docs/manual/90-trouble.md create mode 100644 docs/manual/README.md create mode 100644 docs/manual/build/classroom-manual.html create mode 100644 docs/manual/build/classroom-manual.pdf create mode 100644 docs/manual/images/library.png create mode 100644 docs/manual/images/quick-look.png create mode 100755 scripts/classroom-manual create mode 100755 tests/test-classroom-manual.sh create mode 100755 tools/build-manual diff --git a/config/plasma/applications/ekklesia-manual.desktop b/config/plasma/applications/ekklesia-manual.desktop new file mode 100644 index 0000000..be812c7 --- /dev/null +++ b/config/plasma/applications/ekklesia-manual.desktop @@ -0,0 +1,19 @@ +[Desktop Entry] +Type=Application +Name=Help +GenericName=Classroom manual +Comment=How to use this classroom computer +# Breeze's own. A question mark in a circle is the one mark that needs no +# explaining, and this is the one place in the classroom where borrowing +# from Breeze is better than drawing something of our own. +Icon=help-contents +Exec=/usr/local/bin/classroom-manual +Terminal=false +Categories=Documentation; +StartupNotify=true +# Chrome in application mode reports the app id Chrome derives from the +# --app URL, not this entry, so the panel cannot match the manual window +# to this launcher. That is deliberate and harmless: Help is a Quick +# Launch button, which is a launcher and never a task, so there is no +# button here waiting to be matched. +NoDisplay=false diff --git a/docs/manual/00-welcome.md b/docs/manual/00-welcome.md new file mode 100644 index 0000000..4de6d5c --- /dev/null +++ b/docs/manual/00-welcome.md @@ -0,0 +1,24 @@ +--- +title: Your classroom computer +order: 0 +--- + +This computer is set up for one thing: running your room. It switches +itself on into the classroom, and there is nothing on it to break. + +You do not need a password. You do not need to sign in. You do not need +to shut it down at the end — leave it on. + +## The bar along the bottom + +Everything you need is on the bar at the bottom of the screen. + +The buttons in the middle are the things you can open. Which ones are +there depends on what your room is set up for, so your bar may have +fewer buttons than another room's. + +The right-hand end shows the network, the volume, any messages and the +clock. + +To open something, click its button once. To come back to it later, +click the same button again. diff --git a/docs/manual/10-browser.md b/docs/manual/10-browser.md new file mode 100644 index 0000000..96fc293 --- /dev/null +++ b/docs/manual/10-browser.md @@ -0,0 +1,28 @@ +--- +title: The Browser +order: 10 +--- + +The **Browser** button opens the web: YouTube, a song, a video clip, a +website a lesson sends you to. + +## Using it + +1. Click **Browser** on the bar. +2. Type where you want to go in the bar at the top, or click **Home** to + go back to YouTube. + +The room stays signed in to YouTube, so you do not have to. + +## Playing a video on the television + +Click the full-screen button in the corner of the video, the same as you +would at home. Press **Esc** to come back out of full screen. + +## When you have finished + +Leave it open, or close the window. The next person can open it again +from the bar. + +Anything you sign in to yourself — a personal account, an email — sign +out of before you leave. The computer is shared. diff --git a/docs/manual/20-sound.md b/docs/manual/20-sound.md new file mode 100644 index 0000000..7af160a --- /dev/null +++ b/docs/manual/20-sound.md @@ -0,0 +1,27 @@ +--- +title: Sound and volume +order: 20 +--- + +Sound goes to the television when one is connected, and to the +computer's own speakers when one is not. You do not have to choose. + +## Changing the volume + +Use the volume keys on the keyboard, or click the speaker at the +right-hand end of the bar and drag the slider. + +A large volume bar appears on the screen while you are changing it, so +you can see it from across the room. + +## If there is no sound + +1. Check the volume on the **television** as well as on the computer. + Both have to be up. +2. Check the television is showing this computer, and not something else + plugged into it. +3. If the television was switched on after the computer, the sound may + still be going to the computer's own speakers. Turn the volume down + and back up once; that is enough to move it across. + +If none of that works, tell the office. Do not unplug anything. diff --git a/docs/manual/40-presentations.md b/docs/manual/40-presentations.md new file mode 100644 index 0000000..a3cf72c --- /dev/null +++ b/docs/manual/40-presentations.md @@ -0,0 +1,118 @@ +--- +title: Presentations +order: 40 +feature: PRESENTATIONS_ENABLED +printed-if: If your computer has a Presentations button on the bar +--- + +**Presentations** holds the slides for your sessions. You download a +presentation in the browser once, and from then on it is on this screen, +ready to show on the television. + +## Getting a presentation onto the computer + +1. Click **Browser** and go to wherever the presentation is — your + email, a shared folder, wherever it was sent to you. +2. Download it the way you normally would. +3. Wait until the download has finished. + +That is all. It appears in **Presentations** by itself a few seconds +later. You do not have to save it anywhere special, move it, or open it. + +PowerPoint files and PDF files both work. + +Your download stays in the browser exactly as it was. Nothing here +changes it, moves it or deletes it. + +## Opening the library + +Click **Presentations** on the bar. + +![The Presentations library, with a card for each session](images/library.png) + +Each session is a card with its name and a picture of one of its slides. +Find tonight's by looking at the picture. + +If a session has only just been downloaded, its card says it is being +got ready. It will be ready in a moment. + +## Quick Look — checking the slides first + +**Quick Look** lets you read through a session before you show it to +anybody. + +![Quick Look, showing one slide with Next and Previous underneath](images/quick-look.png) + +* **Next** and **Previous** move through the slides. +* **Back to Presentations** returns to the library. +* **Present** starts the slideshow straight away. + +Looking through a session in Quick Look does not count as having +presented it. + +## Changing the picture on the card + +The first slide of a session is often the same opening slide as every +other session, which makes the cards hard to tell apart. Use the slide +that names the session instead — usually the second one. + +1. Click **Quick Look**. +2. Press **Next** until you reach the slide you want. +3. Click **Use this slide as the picture**. + +The card changes straight away, and stays that way — after you close the +library, and after the computer is switched off and on again. + +Choosing the picture does not change where the slideshow starts. It +always starts at the first slide. + +## Showing a presentation + +Click **Present**, on the card or inside Quick Look. + +The slides fill the whole screen. Nothing else is on it. + +| To do this | Press | +| --- | --- | +| Next slide | **Space**, the **right arrow**, the **down arrow** or **Page Down** | +| Previous slide | The **left arrow**, the **up arrow** or **Page Up** | +| First slide | **Home** | +| Last slide | **End** | +| Finish | **Esc** | + +A presentation remote works too — the forward and back buttons on the +clicker are the same as Page Down and Page Up. + +## Finishing + +Press **Esc**. + +You come straight back to the library. Going past the last slide does +the same thing. + +## Removing a presentation + +Click **Remove** on its card, and confirm. + +That takes it off this screen. Your download in the browser is not +touched, so you can always get it back by downloading it again. + +## Presentations you no longer need + +The computer tidies up after itself, so the library does not slowly fill +up with old sessions. + +A session you have shown is kept for a month after the last time you +showed it. Showing it again starts that month over. + +A session you downloaded but never showed is kept for six months. + +You do not have to do anything about this, and nothing disappears while +you are using it. + +## If a presentation will not open + +Its card says so, and offers only **Remove**. Usually the file did not +download properly. + +Download it again. Your original download is still there either way. diff --git a/docs/manual/50-wonderink.md b/docs/manual/50-wonderink.md new file mode 100644 index 0000000..408b996 --- /dev/null +++ b/docs/manual/50-wonderink.md @@ -0,0 +1,35 @@ +--- +title: Wonder Ink +order: 50 +feature: WONDERINK_ENABLED +printed-if: If Wonder Ink is available in your classroom +--- + +**Wonder Ink** opens the lesson. It is its own application, not the +browser, and it opens straight into the lesson site. + +## Using it + +1. Click **Wonder Ink** on the bar. +2. Sign in with your Wonder Ink account. + +A lesson often opens several tabs — the lesson, its video, an activity. +They appear in a row across the top. Click a tab to move between them, +and the **×** on a tab to close it. + +There is no address bar, on purpose. Wonder Ink can take you anywhere +the lesson needs to go. + +## Worksheets and colouring pages + +Click the worksheet link in the lesson. It opens in a new tab, ready to +read or print. + +Worksheets you download are kept separately from everything else on the +computer, so they never get mixed up with anything. + +## When you have finished + +**Close Wonder Ink at the end of your session.** Closing it signs you +out, so the next teacher in this room starts with their own account and +not yours. diff --git a/docs/manual/60-parent-call.md b/docs/manual/60-parent-call.md new file mode 100644 index 0000000..489f959 --- /dev/null +++ b/docs/manual/60-parent-call.md @@ -0,0 +1,28 @@ +--- +title: Parent Call +order: 60 +feature: PARENT_CALL_ENABLED +printed-if: If your computer has a Parent Call button in the bottom-left corner +--- + +**Parent Call** asks the sanctuary to fetch a child's grown-up, without +you leaving the room. + +## Using it + +1. Click **Parent Call**, in the bottom-left corner of the screen. +2. Type the child's name. +3. Click **Send**. + +The card tells you what is happening. **Sent to sanctuary** means +somebody there has it. + +## What it does and does not promise + +**Sent to sanctuary** means the message arrived. It does not mean it has +been read out yet — somebody there decides when. + +If it cannot reach the sanctuary it says so plainly. It will never tell +you a call went through when it did not. + +If that happens, send somebody. diff --git a/docs/manual/70-dvd.md b/docs/manual/70-dvd.md new file mode 100644 index 0000000..d690f50 --- /dev/null +++ b/docs/manual/70-dvd.md @@ -0,0 +1,30 @@ +--- +title: Playing a DVD +order: 70 +feature: DVD_ENABLED +printed-if: If your computer has a DVD button and a disc drive +--- + +## Playing a disc + +1. Put the disc in the drive. +2. Wait for **DVD Ready** to appear. +3. Click **DVD** on the bar. + +The disc fills the screen and plays on the television. Discs never start +on their own — it always takes a button press. + +## While it is playing + +Move the mouse to the very bottom of the screen and a row of controls +slides up: previous chapter, play and pause, next chapter, the disc +menu, eject, and close. + +**Space** also plays and pauses, and the arrow keys move around the disc +menu. + +## Finishing + +Click **✕** on that row to stop, or **⏏** to stop and eject the disc. + +Take the disc with you. diff --git a/docs/manual/80-printing.md b/docs/manual/80-printing.md new file mode 100644 index 0000000..0f3d162 --- /dev/null +++ b/docs/manual/80-printing.md @@ -0,0 +1,23 @@ +--- +title: Printing +order: 80 +--- + +Printing goes to the church printer. There is nothing to choose and +nothing to set up. + +## Printing something + +1. Press **Ctrl+P**, or use the **Print** button on the page you are + looking at. +2. Check the number of copies. +3. Click **Print**. + +## If nothing comes out + +The printer may be off, out of paper, or somebody else's job may be +ahead of yours. Your job waits in the queue and prints when the printer +is ready again — you do not need to send it twice. + +Sending it again two or three times is how ten copies of the same +worksheet appear later. diff --git a/docs/manual/90-trouble.md b/docs/manual/90-trouble.md new file mode 100644 index 0000000..8a49862 --- /dev/null +++ b/docs/manual/90-trouble.md @@ -0,0 +1,35 @@ +--- +title: If something goes wrong +order: 90 +--- + +Almost nothing here needs a technical person, and nothing you can do +from the keyboard will break the computer. + +## The screen is black + +Move the mouse or press a key. If it stays black, check the television +is on and showing this computer. + +## A window will not close + +Click its button on the bar to bring it back, and close it normally. + +## The bar has gone + +Wait a few seconds; it puts itself back. + +If it does not, switch the computer off at the power button, wait ten +seconds, and switch it on again. It comes back to the same screen it +always does. + +## Something asks for a password + +Nothing a teacher needs to do asks for a password. If a password box +appears, you have reached something you did not mean to — press **Esc** +or click **Cancel**, and carry on. + +## It is still wrong + +Tell the office, and say **which room** and **what you were doing when +it happened**. That second part is the one that saves everybody time. diff --git a/docs/manual/README.md b/docs/manual/README.md new file mode 100644 index 0000000..62e67e6 --- /dev/null +++ b/docs/manual/README.md @@ -0,0 +1,85 @@ +# The teacher's manual + +**This directory is the only place teacher instructions are written.** + +Everything a teacher reads comes out of the `.md` files beside this one: + +* the **digital manual** on each classroom computer, which shows only the + features that computer actually has; and +* the **printable PDF**, which covers every feature with wording that + stays true in a room that does not have all of them. + +Both are built by `tools/build-manual` from these files. There is no +second copy to keep in step, and there is no per-room manual: a room +differs from another room by its `device.conf`, and the manual reads the +same setting the panel does. + +## Writing a page + +Each page is Markdown with a small header: + +```markdown +--- +title: Presentations +order: 40 +feature: PRESENTATIONS_ENABLED +printed-if: If your computer has a Presentations button +--- + +## Downloading a presentation +... +``` + +| Field | Means | +| --- | --- | +| `title` | The heading, and the entry in the contents | +| `order` | Where it comes. Common pages are 00–39, optional ones 40+ | +| `feature` | The `device.conf` setting that decides whether a computer shows this page. Leave it out for a page every room gets | +| `printed-if` | The conditional sentence the printed manual puts above the page. Required whenever `feature` is set | + +Only `##` and `###` headings, ordinary paragraphs, `*` bullets, `1.` +numbered steps, simple `|` tables, `![caption](images/x.png)` pictures +and **bold** are supported. That is deliberate: the same text has to come +out well as a web page and as print, and a manual for teachers does not +need anything else. + +## Writing style + +The existing pages set it, and it matters more than the format: + +* Say what the teacher does, not what the computer does. +* One instruction per step, in the order they happen. +* Name things the way the screen names them — **Present**, **Quick + Look**, the **bar** along the bottom. +* No paths, no package names, no settings files, no version numbers. +* If something can go wrong, say what to do about it in the same breath. + +## Building + +``` +tools/build-manual # both, into docs/manual/build/ +tools/build-manual --check # verify only; what the release runs +``` + +`tools/build-release.sh` builds and checks the manual on every release, so +the digital and printed versions cannot drift apart. The check fails the +build if a page declares a `feature` with no `printed-if`, if it names a +feature `device.conf` does not have, or if the built files are missing. + +## Screenshots + +Pictures live in `docs/manual/images/`. They are captured from the real +interface with `tools/lab/lab shot`, never drawn or mocked up. A manual +showing a screen that does not exist is worse than one with no pictures. + +They are **embedded** into the digital manual as data URIs, so it stays a +single file that works wherever it is copied and with the network +unplugged. A picture named by a page that is not in `images/` fails the +build, as does one with no caption. + +**The printed manual prints the caption, not the picture** — as +`[ Picture: ... — see the computer's own Help ]`. Embedding a PNG in a +PDF means decoding it and writing an `/XObject`, and this builder exists +so that a release needs nothing but Python. If the printed manual ever +needs real pictures, that is the thing to add, and it is a contained +change: `render_pdf` is the only function that would move. diff --git a/docs/manual/build/classroom-manual.html b/docs/manual/build/classroom-manual.html new file mode 100644 index 0000000..fc55c82 --- /dev/null +++ b/docs/manual/build/classroom-manual.html @@ -0,0 +1,214 @@ + + + + + +Classroom manual + + + +
+

Classroom manual

+

River of Glory · Revision 22 September 2026

+ +
+

Your classroom computer

+

This computer is set up for one thing: running your room. It switches itself on into the classroom, and there is nothing on it to break.

+

You do not need a password. You do not need to sign in. You do not need to shut it down at the end — leave it on.

+

The bar along the bottom

+

Everything you need is on the bar at the bottom of the screen.

+

The buttons in the middle are the things you can open. Which ones are there depends on what your room is set up for, so your bar may have fewer buttons than another room's.

+

The right-hand end shows the network, the volume, any messages and the clock.

+

To open something, click its button once. To come back to it later, click the same button again.

+

Back to the top

+
+
+

The Browser

+

The Browser button opens the web: YouTube, a song, a video clip, a website a lesson sends you to.

+

Using it

+
  1. Click Browser on the bar.
  2. Type where you want to go in the bar at the top, or click Home to go back to YouTube.
+

The room stays signed in to YouTube, so you do not have to.

+

Playing a video on the television

+

Click the full-screen button in the corner of the video, the same as you would at home. Press Esc to come back out of full screen.

+

When you have finished

+

Leave it open, or close the window. The next person can open it again from the bar.

+

Anything you sign in to yourself — a personal account, an email — sign out of before you leave. The computer is shared.

+

Back to the top

+
+
+

Sound and volume

+

Sound goes to the television when one is connected, and to the computer's own speakers when one is not. You do not have to choose.

+

Changing the volume

+

Use the volume keys on the keyboard, or click the speaker at the right-hand end of the bar and drag the slider.

+

A large volume bar appears on the screen while you are changing it, so you can see it from across the room.

+

If there is no sound

+
  1. Check the volume on the television as well as on the computer. Both have to be up.
  2. Check the television is showing this computer, and not something else plugged into it.
  3. If the television was switched on after the computer, the sound may still be going to the computer's own speakers. Turn the volume down and back up once; that is enough to move it across.
+

If none of that works, tell the office. Do not unplug anything.

+

Back to the top

+
+
+

Presentations

+

Presentations holds the slides for your sessions. You download a presentation in the browser once, and from then on it is on this screen, ready to show on the television.

+

Getting a presentation onto the computer

+
  1. Click Browser and go to wherever the presentation is — your email, a shared folder, wherever it was sent to you.
  2. Download it the way you normally would.
  3. Wait until the download has finished.
+

That is all. It appears in Presentations by itself a few seconds later. You do not have to save it anywhere special, move it, or open it.

+

PowerPoint files and PDF files both work.

+

Your download stays in the browser exactly as it was. Nothing here changes it, moves it or deletes it.

+

Opening the library

+

Click Presentations on the bar.

+
The Presentations library, with a card for each session
The Presentations library, with a card for each session
+

Each session is a card with its name and a picture of one of its slides. Find tonight's by looking at the picture.

+

If a session has only just been downloaded, its card says it is being got ready. It will be ready in a moment.

+

Quick Look — checking the slides first

+

Quick Look lets you read through a session before you show it to anybody.

+
Quick Look, showing one slide with Next and Previous underneath
Quick Look, showing one slide with Next and Previous underneath
+
  • Next and Previous move through the slides.
  • Back to Presentations returns to the library.
  • Present starts the slideshow straight away.
+

Looking through a session in Quick Look does not count as having presented it.

+

Changing the picture on the card

+

The first slide of a session is often the same opening slide as every other session, which makes the cards hard to tell apart. Use the slide that names the session instead — usually the second one.

+
  1. Click Quick Look.
  2. Press Next until you reach the slide you want.
  3. Click Use this slide as the picture.
+

The card changes straight away, and stays that way — after you close the library, and after the computer is switched off and on again.

+

Choosing the picture does not change where the slideshow starts. It always starts at the first slide.

+

Showing a presentation

+

Click Present, on the card or inside Quick Look.

+

The slides fill the whole screen. Nothing else is on it.

+
To do thisPress
Next slideSpace, the right arrow, the down arrow or Page Down
Previous slideThe left arrow, the up arrow or Page Up
First slideHome
Last slideEnd
FinishEsc
+

A presentation remote works too — the forward and back buttons on the clicker are the same as Page Down and Page Up.

+

Finishing

+

Press Esc.

+

You come straight back to the library. Going past the last slide does the same thing.

+

Removing a presentation

+

Click Remove on its card, and confirm.

+

That takes it off this screen. Your download in the browser is not touched, so you can always get it back by downloading it again.

+

Presentations you no longer need

+

The computer tidies up after itself, so the library does not slowly fill up with old sessions.

+

A session you have shown is kept for a month after the last time you showed it. Showing it again starts that month over.

+

A session you downloaded but never showed is kept for six months.

+

You do not have to do anything about this, and nothing disappears while you are using it.

+

If a presentation will not open

+

Its card says so, and offers only Remove. Usually the file did not download properly.

+

Download it again. Your original download is still there either way.

+

Back to the top

+
+
+

Wonder Ink

+

Wonder Ink opens the lesson. It is its own application, not the browser, and it opens straight into the lesson site.

+

Using it

+
  1. Click Wonder Ink on the bar.
  2. Sign in with your Wonder Ink account.
+

A lesson often opens several tabs — the lesson, its video, an activity. They appear in a row across the top. Click a tab to move between them, and the × on a tab to close it.

+

There is no address bar, on purpose. Wonder Ink can take you anywhere the lesson needs to go.

+

Worksheets and colouring pages

+

Click the worksheet link in the lesson. It opens in a new tab, ready to read or print.

+

Worksheets you download are kept separately from everything else on the computer, so they never get mixed up with anything.

+

When you have finished

+

Close Wonder Ink at the end of your session. Closing it signs you out, so the next teacher in this room starts with their own account and not yours.

+

Back to the top

+
+
+

Parent Call

+

Parent Call asks the sanctuary to fetch a child's grown-up, without you leaving the room.

+

Using it

+
  1. Click Parent Call, in the bottom-left corner of the screen.
  2. Type the child's name.
  3. Click Send.
+

The card tells you what is happening. Sent to sanctuary means somebody there has it.

+

What it does and does not promise

+

Sent to sanctuary means the message arrived. It does not mean it has been read out yet — somebody there decides when.

+

If it cannot reach the sanctuary it says so plainly. It will never tell you a call went through when it did not.

+

If that happens, send somebody.

+

Back to the top

+
+
+

Playing a DVD

+

Playing a disc

+
  1. Put the disc in the drive.
  2. Wait for DVD Ready to appear.
  3. Click DVD on the bar.
+

The disc fills the screen and plays on the television. Discs never start on their own — it always takes a button press.

+

While it is playing

+

Move the mouse to the very bottom of the screen and a row of controls slides up: previous chapter, play and pause, next chapter, the disc menu, eject, and close.

+

Space also plays and pauses, and the arrow keys move around the disc menu.

+

Finishing

+

Click ✕ on that row to stop, or ⏏ to stop and eject the disc.

+

Take the disc with you.

+

Back to the top

+
+
+

Printing

+

Printing goes to the church printer. There is nothing to choose and nothing to set up.

+

Printing something

+
  1. Press Ctrl+P, or use the Print button on the page you are looking at.
  2. Check the number of copies.
  3. Click Print.
+

If nothing comes out

+

The printer may be off, out of paper, or somebody else's job may be ahead of yours. Your job waits in the queue and prints when the printer is ready again — you do not need to send it twice.

+

Sending it again two or three times is how ten copies of the same worksheet appear later.

+

Back to the top

+
+
+

If something goes wrong

+

Almost nothing here needs a technical person, and nothing you can do from the keyboard will break the computer.

+

The screen is black

+

Move the mouse or press a key. If it stays black, check the television is on and showing this computer.

+

A window will not close

+

Click its button on the bar to bring it back, and close it normally.

+

The bar has gone

+

Wait a few seconds; it puts itself back.

+

If it does not, switch the computer off at the power button, wait ten seconds, and switch it on again. It comes back to the same screen it always does.

+

Something asks for a password

+

Nothing a teacher needs to do asks for a password. If a password box appears, you have reached something you did not mean to — press Esc or click Cancel, and carry on.

+

It is still wrong

+

Tell the office, and say which room and what you were doing when it happened. That second part is the one that saves everybody time.

+

Back to the top

+
+
+ + \ No newline at end of file diff --git a/docs/manual/build/classroom-manual.pdf b/docs/manual/build/classroom-manual.pdf new file mode 100644 index 0000000..04c0265 --- /dev/null +++ b/docs/manual/build/classroom-manual.pdf @@ -0,0 +1,362 @@ +%PDF-1.4 +1 0 obj +<< /Type /Catalog /Pages 2 0 R >> +endobj +2 0 obj +<< /Type /Pages /Kids [3 0 R 4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R] /Count 11 >> +endobj +3 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 14 0 R >> +endobj +4 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 15 0 R >> +endobj +5 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 16 0 R >> +endobj +6 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 17 0 R >> +endobj +7 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 18 0 R >> +endobj +8 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 19 0 R >> +endobj +9 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 20 0 R >> +endobj +10 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 21 0 R >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 22 0 R >> +endobj +12 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 23 0 R >> +endobj +13 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 25 0 R /F2 26 0 R >> >> /Contents 24 0 R >> +endobj +14 0 obj +<< /Length 765 >> +stream +BT /F2 26 Tf 1 0 0 1 62.0 720.0 Tm (Classroom manual) Tj ET +BT /F1 13 Tf 1 0 0 1 62.0 677.1 Tm (River of Glory) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 656.6 Tm (Revision 22 September 2026) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 623.7 Tm (This manual covers every classroom computer in the church. Not every room has every button, so) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 608.8 Tm (some pages begin by saying when they apply. If your computer does not have what a page describes,) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 593.9 Tm (that page is not for your room.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 569.0 Tm (Keep this copy in the room. If the date above is older than the one on the computer's own Help, ask) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 554.1 Tm (the office for a new one.) Tj ET +endstream +endobj +15 0 obj +<< /Length 1250 >> +stream +BT /F2 17 Tf 1 0 0 1 62.0 774.0 Tm (Your classroom computer) Tj ET +0.75 w 0.6 0.6 0.6 RG 62 751.9 m 533 751.9 l S +BT /F1 10.5 Tf 1 0 0 1 62.0 733.9 Tm (This computer is set up for one thing: running your room. It switches itself on into the) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 719.0 Tm (classroom, and there is nothing on it to break.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 699.0 Tm (You do not need a password. You do not need to sign in. You do not need to shut it down at the end -) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 684.1 Tm (leave it on.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 656.2 Tm (The bar along the bottom) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 635.5 Tm (Everything you need is on the bar at the bottom of the screen.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 615.6 Tm (The buttons in the middle are the things you can open. Which ones are there depends on what your) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 600.7 Tm (room is set up for, so your bar may have fewer buttons than another room's.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 580.7 Tm (The right-hand end shows the network, the volume, any messages and the clock.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 560.8 Tm (To open something, click its button once. To come back to it later, click the same button again.) Tj ET +endstream +endobj +16 0 obj +<< /Length 1408 >> +stream +BT /F2 17 Tf 1 0 0 1 62.0 774.0 Tm (The Browser) Tj ET +0.75 w 0.6 0.6 0.6 RG 62 751.9 m 533 751.9 l S +BT /F1 10.5 Tf 1 0 0 1 62.0 733.9 Tm (The Browser button opens the web: YouTube, a song, a video clip, a website a lesson sends you to.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 706.0 Tm (Using it) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 685.2 Tm (1.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 685.2 Tm (Click Browser on the bar.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 670.3 Tm (2.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 670.3 Tm (Type where you want to go in the bar at the top, or click Home to go back to YouTube.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 650.4 Tm (The room stays signed in to YouTube, so you do not have to.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 622.5 Tm (Playing a video on the television) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 601.7 Tm (Click the full-screen button in the corner of the video, the same as you would at home. Press Esc) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 586.8 Tm (to come back out of full screen.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 558.9 Tm (When you have finished) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 538.2 Tm (Leave it open, or close the window. The next person can open it again from the bar.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 518.2 Tm (Anything you sign in to yourself - a personal account, an email - sign out of before you leave.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 503.3 Tm (The computer is shared.) Tj ET +endstream +endobj +17 0 obj +<< /Length 1694 >> +stream +BT /F2 17 Tf 1 0 0 1 62.0 774.0 Tm (Sound and volume) Tj ET +0.75 w 0.6 0.6 0.6 RG 62 751.9 m 533 751.9 l S +BT /F1 10.5 Tf 1 0 0 1 62.0 733.9 Tm (Sound goes to the television when one is connected, and to the computer's own speakers when one) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 719.0 Tm (is not. You do not have to choose.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 691.0 Tm (Changing the volume) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 670.3 Tm (Use the volume keys on the keyboard, or click the speaker at the right-hand end of the bar and) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 655.4 Tm (drag the slider.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 635.5 Tm (A large volume bar appears on the screen while you are changing it, so you can see it from across) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 620.6 Tm (the room.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 592.7 Tm (If there is no sound) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 571.9 Tm (1.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 571.9 Tm (Check the volume on the television as well as on the computer. Both have to be up.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 557.0 Tm (2.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 557.0 Tm (Check the television is showing this computer, and not something else plugged into it.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 542.1 Tm (3.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 542.1 Tm (If the television was switched on after the computer, the sound may still be going to the) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 527.2 Tm (computer's own speakers. Turn the volume down and back up once; that is enough to move it) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 512.3 Tm (across.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 492.4 Tm (If none of that works, tell the office. Do not unplug anything.) Tj ET +endstream +endobj +18 0 obj +<< /Length 4084 >> +stream +BT /F2 17 Tf 1 0 0 1 62.0 774.0 Tm (Presentations) Tj ET +0.75 w 0.6 0.6 0.6 RG 62 751.9 m 533 751.9 l S +BT /F2 10.5 Tf 1 0 0 1 62.0 733.9 Tm (If your computer has a Presentations button on the bar.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 713.0 Tm (Presentations holds the slides for your sessions. You download a presentation in the browser) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 698.0 Tm (once, and from then on it is on this screen, ready to show on the television.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 670.1 Tm (Getting a presentation onto the computer) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 649.4 Tm (1.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 649.4 Tm (Click Browser and go to wherever the presentation is - your email, a shared folder,) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 634.5 Tm (wherever it was sent to you.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 619.6 Tm (2.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 619.6 Tm (Download it the way you normally would.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 604.7 Tm (3.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 604.7 Tm (Wait until the download has finished.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 584.7 Tm (That is all. It appears in Presentations by itself a few seconds later. You do not have to save) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 569.8 Tm (it anywhere special, move it, or open it.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 549.9 Tm (PowerPoint files and PDF files both work.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 530.0 Tm (Your download stays in the browser exactly as it was. Nothing here changes it, moves it or) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 515.1 Tm (deletes it.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 487.2 Tm (Opening the library) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 466.4 Tm (Click Presentations on the bar.) Tj ET +BT /F1 9.5 Tf 1 0 0 1 62.0 446.5 Tm ([ Picture: The Presentations library, with a card for each session - see the computer's own Help ]) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 428.0 Tm (Each session is a card with its name and a picture of one of its slides. Find tonight's by looking) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 413.1 Tm (at the picture.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 393.2 Tm (If a session has only just been downloaded, its card says it is being got ready. It will be ready) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 378.3 Tm (in a moment.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 350.4 Tm (Quick Look - checking the slides first) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 329.7 Tm (Quick Look lets you read through a session before you show it to anybody.) Tj ET +BT /F1 9.5 Tf 1 0 0 1 62.0 309.7 Tm ([ Picture: Quick Look, showing one slide with Next and Previous underneath - see the computer's own Help ]) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 291.3 Tm (-) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 291.3 Tm (Next and Previous move through the slides.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 276.3 Tm (-) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 276.3 Tm (Back to Presentations returns to the library.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 261.4 Tm (-) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 261.4 Tm (Present starts the slideshow straight away.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 241.5 Tm (Looking through a session in Quick Look does not count as having presented it.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 213.6 Tm (Changing the picture on the card) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 192.9 Tm (The first slide of a session is often the same opening slide as every other session, which makes) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 178.0 Tm (the cards hard to tell apart. Use the slide that names the session instead - usually the second) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 163.0 Tm (one.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 143.1 Tm (1.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 143.1 Tm (Click Quick Look.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 128.2 Tm (2.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 128.2 Tm (Press Next until you reach the slide you want.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 113.3 Tm (3.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 113.3 Tm (Click Use this slide as the picture.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 93.4 Tm (The card changes straight away, and stays that way - after you close the library, and after the) Tj ET +endstream +endobj +19 0 obj +<< /Length 3089 >> +stream +BT /F1 10.5 Tf 1 0 0 1 62.0 780.0 Tm (computer is switched off and on again.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 760.1 Tm (Choosing the picture does not change where the slideshow starts. It always starts at the first) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 745.2 Tm (slide.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 717.3 Tm (Showing a presentation) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 696.5 Tm (Click Present, on the card or inside Quick Look.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 676.6 Tm (The slides fill the whole screen. Nothing else is on it.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 656.7 Tm ( ) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 656.7 Tm (TO DO THIS PRESS) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 641.8 Tm ( ) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 641.8 Tm (Next slide Space, the right arrow, the down arrow or Page Down) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 626.9 Tm ( ) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 626.9 Tm (Previous slide The left arrow, the up arrow or Page Up) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 612.0 Tm ( ) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 612.0 Tm (First slide Home) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 597.1 Tm ( ) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 597.1 Tm (Last slide End) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 582.2 Tm ( ) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 582.2 Tm (Finish Esc) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 562.2 Tm (A presentation remote works too - the forward and back buttons on the clicker are the same as) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 547.3 Tm (Page Down and Page Up.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 519.4 Tm (Finishing) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 498.7 Tm (Press Esc.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 478.8 Tm (You come straight back to the library. Going past the last slide does the same thing.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 450.9 Tm (Removing a presentation) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 430.1 Tm (Click Remove on its card, and confirm.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 410.2 Tm (That takes it off this screen. Your download in the browser is not touched, so you can always get) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 395.3 Tm (it back by downloading it again.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 367.4 Tm (Presentations you no longer need) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 346.6 Tm (The computer tidies up after itself, so the library does not slowly fill up with old sessions.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 326.7 Tm (A session you have shown is kept for a month after the last time you showed it. Showing it again) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 311.8 Tm (starts that month over.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 291.9 Tm (A session you downloaded but never showed is kept for six months.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 272.0 Tm (You do not have to do anything about this, and nothing disappears while you are using it.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 244.1 Tm (If a presentation will not open) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 223.3 Tm (Its card says so, and offers only Remove. Usually the file did not download properly.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 203.4 Tm (Download it again. Your original download is still there either way.) Tj ET +endstream +endobj +20 0 obj +<< /Length 1838 >> +stream +BT /F2 17 Tf 1 0 0 1 62.0 774.0 Tm (Wonder Ink) Tj ET +0.75 w 0.6 0.6 0.6 RG 62 751.9 m 533 751.9 l S +BT /F2 10.5 Tf 1 0 0 1 62.0 733.9 Tm (If Wonder Ink is available in your classroom.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 713.0 Tm (Wonder Ink opens the lesson. It is its own application, not the browser, and it opens straight) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 698.0 Tm (into the lesson site.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 670.1 Tm (Using it) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 649.4 Tm (1.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 649.4 Tm (Click Wonder Ink on the bar.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 634.5 Tm (2.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 634.5 Tm (Sign in with your Wonder Ink account.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 614.6 Tm (A lesson often opens several tabs - the lesson, its video, an activity. They appear in a row) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 599.7 Tm (across the top. Click a tab to move between them, and the x on a tab to close it.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 579.7 Tm (There is no address bar, on purpose. Wonder Ink can take you anywhere the lesson needs to go.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 551.8 Tm (Worksheets and colouring pages) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 531.1 Tm (Click the worksheet link in the lesson. It opens in a new tab, ready to read or print.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 511.2 Tm (Worksheets you download are kept separately from everything else on the computer, so they never) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 496.3 Tm (get mixed up with anything.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 468.4 Tm (When you have finished) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 447.6 Tm (Close Wonder Ink at the end of your session. Closing it signs you out, so the next teacher in) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 432.7 Tm (this room starts with their own account and not yours.) Tj ET +endstream +endobj +21 0 obj +<< /Length 1475 >> +stream +BT /F2 17 Tf 1 0 0 1 62.0 774.0 Tm (Parent Call) Tj ET +0.75 w 0.6 0.6 0.6 RG 62 751.9 m 533 751.9 l S +BT /F2 10.5 Tf 1 0 0 1 62.0 733.9 Tm (If your computer has a Parent Call button in the bottom-left corner.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 713.0 Tm (Parent Call asks the sanctuary to fetch a child's grown-up, without you leaving the room.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 685.0 Tm (Using it) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 664.3 Tm (1.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 664.3 Tm (Click Parent Call, in the bottom-left corner of the screen.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 649.4 Tm (2.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 649.4 Tm (Type the child's name.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 634.5 Tm (3.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 634.5 Tm (Click Send.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 614.6 Tm (The card tells you what is happening. Sent to sanctuary means somebody there has it.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 586.7 Tm (What it does and does not promise) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 565.9 Tm (Sent to sanctuary means the message arrived. It does not mean it has been read out yet - somebody) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 551.0 Tm (there decides when.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 531.1 Tm (If it cannot reach the sanctuary it says so plainly. It will never tell you a call went through) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 516.2 Tm (when it did not.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 496.3 Tm (If that happens, send somebody.) Tj ET +endstream +endobj +22 0 obj +<< /Length 1500 >> +stream +BT /F2 17 Tf 1 0 0 1 62.0 774.0 Tm (Playing a DVD) Tj ET +0.75 w 0.6 0.6 0.6 RG 62 751.9 m 533 751.9 l S +BT /F2 10.5 Tf 1 0 0 1 62.0 733.9 Tm (If your computer has a DVD button and a disc drive.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 705.0 Tm (Playing a disc) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 684.2 Tm (1.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 684.2 Tm (Put the disc in the drive.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 669.3 Tm (2.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 669.3 Tm (Wait for DVD Ready to appear.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 654.4 Tm (3.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 654.4 Tm (Click DVD on the bar.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 634.5 Tm (The disc fills the screen and plays on the television. Discs never start on their own - it always) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 619.6 Tm (takes a button press.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 591.7 Tm (While it is playing) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 570.9 Tm (Move the mouse to the very bottom of the screen and a row of controls slides up: previous chapter,) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 556.0 Tm (play and pause, next chapter, the disc menu, eject, and close.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 536.1 Tm (Space also plays and pauses, and the arrow keys move around the disc menu.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 508.2 Tm (Finishing) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 487.4 Tm (Click X on that row to stop, or eject to stop and eject the disc.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 467.5 Tm (Take the disc with you.) Tj ET +endstream +endobj +23 0 obj +<< /Length 1166 >> +stream +BT /F2 17 Tf 1 0 0 1 62.0 774.0 Tm (Printing) Tj ET +0.75 w 0.6 0.6 0.6 RG 62 751.9 m 533 751.9 l S +BT /F1 10.5 Tf 1 0 0 1 62.0 733.9 Tm (Printing goes to the church printer. There is nothing to choose and nothing to set up.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 706.0 Tm (Printing something) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 685.2 Tm (1.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 685.2 Tm (Press Ctrl+P, or use the Print button on the page you are looking at.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 670.3 Tm (2.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 670.3 Tm (Check the number of copies.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 78.0 655.4 Tm (3.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 94.0 655.4 Tm (Click Print.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 627.5 Tm (If nothing comes out) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 606.7 Tm (The printer may be off, out of paper, or somebody else's job may be ahead of yours. Your job waits) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 591.8 Tm (in the queue and prints when the printer is ready again - you do not need to send it twice.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 571.9 Tm (Sending it again two or three times is how ten copies of the same worksheet appear later.) Tj ET +endstream +endobj +24 0 obj +<< /Length 1766 >> +stream +BT /F2 17 Tf 1 0 0 1 62.0 774.0 Tm (If something goes wrong) Tj ET +0.75 w 0.6 0.6 0.6 RG 62 751.9 m 533 751.9 l S +BT /F1 10.5 Tf 1 0 0 1 62.0 733.9 Tm (Almost nothing here needs a technical person, and nothing you can do from the keyboard will) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 719.0 Tm (break the computer.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 691.0 Tm (The screen is black) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 670.3 Tm (Move the mouse or press a key. If it stays black, check the television is on and showing this) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 655.4 Tm (computer.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 627.5 Tm (A window will not close) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 606.7 Tm (Click its button on the bar to bring it back, and close it normally.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 578.8 Tm (The bar has gone) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 558.1 Tm (Wait a few seconds; it puts itself back.) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 538.2 Tm (If it does not, switch the computer off at the power button, wait ten seconds, and switch it on) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 523.2 Tm (again. It comes back to the same screen it always does.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 495.3 Tm (Something asks for a password) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 474.6 Tm (Nothing a teacher needs to do asks for a password. If a password box appears, you have reached) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 459.7 Tm (something you did not mean to - press Esc or click Cancel, and carry on.) Tj ET +BT /F2 12.5 Tf 1 0 0 1 62.0 431.8 Tm (It is still wrong) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 411.0 Tm (Tell the office, and say which room and what you were doing when it happened. That second part is) Tj ET +BT /F1 10.5 Tf 1 0 0 1 62.0 396.1 Tm (the one that saves everybody time.) Tj ET +endstream +endobj +25 0 obj +<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> +endobj +26 0 obj +<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >> +endobj +xref +0 27 +0000000000 65535 f +0000000009 00000 n +0000000058 00000 n +0000000180 00000 n +0000000319 00000 n +0000000458 00000 n +0000000597 00000 n +0000000736 00000 n +0000000875 00000 n +0000001014 00000 n +0000001153 00000 n +0000001293 00000 n +0000001433 00000 n +0000001573 00000 n +0000001713 00000 n +0000002530 00000 n +0000003833 00000 n +0000005294 00000 n +0000007041 00000 n +0000011178 00000 n +0000014320 00000 n +0000016211 00000 n +0000017739 00000 n +0000019292 00000 n +0000020511 00000 n +0000022330 00000 n +0000022428 00000 n +trailer +<< /Size 27 /Root 1 0 R >> +startxref +22531 +%%EOF \ No newline at end of file diff --git a/docs/manual/images/library.png b/docs/manual/images/library.png new file mode 100644 index 0000000000000000000000000000000000000000..0d5503fe10b79952d0b80db5a47f89d6d00d71d4 GIT binary patch literal 52358 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

K? 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 literal 0 HcmV?d00001 diff --git a/docs/testing.md b/docs/testing.md index 613a1c7..b81008c 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -473,3 +473,103 @@ With `LIVE_ENABLED=true`: single after reinstall - [ ] `tools/validate-trixie-release.sh` passes against the 0.1.37 package and the 0.1.36 package + +## Presentations — 0.2.6, the prayer-room laptop + +Everything here needs the real television, the real keyboard and the +pastor's own session files. The automated tests and the Plasma lab cover +the logic and the windows; none of them can tell you that Session 7's +text is the right size from the back of the room. + +Enable it first, and reboot: + +``` +sudo sed -i 's/^PRESENTATIONS_ENABLED=.*/PRESENTATIONS_ENABLED=true/' \ + /etc/ekklesia-classroom/device.conf +sudo reboot +``` + +### Download + +- [ ] The bar has a **Presentations** button, after the Browser +- [ ] Wonder Ink and Parent Call are still absent from this laptop +- [ ] Download tonight's session in ordinary Chrome +- [ ] It does **not** appear in the library while it is still downloading +- [ ] It appears by itself within a few seconds of the download finishing +- [ ] The title is readable — not a hash, not a long filename +- [ ] The original download is still in Chrome's downloads, untouched +- [ ] Download the **PDF and the PPTX** of the same session: two cards, + the second titled `(2)`, neither overwriting the other + +### Quick Look + +- [ ] **Quick Look** opens and shows slide 1 +- [ ] **Next** and **Previous** move through every slide +- [ ] The text and layout are right — check a slide with a Bible verse +- [ ] Go to the session-number slide, usually slide 2 +- [ ] **Use this slide as the picture** changes the card +- [ ] **Back to Presentations** returns without presenting +- [ ] Close the library, open it again: the chosen picture is still there +- [ ] Reboot, open it again: still there +- [ ] `classroom-presentation-import --status` shows `presented never` + +That last line is the one that matters. Quick Look, browsing every slide +and changing the picture must not have counted as a presentation. + +### Present + +- [ ] **Present** from the card fills the **external television** +- [ ] It starts at slide **1**, not at the slide chosen for the picture +- [ ] **Present** from inside Quick Look does the same +- [ ] Space, the arrow keys and Page Up/Page Down all move slides +- [ ] The presentation remote's forward and back buttons work +- [ ] Fonts and layout are right on the television, from the back +- [ ] **Esc** returns to the library — **not** the Impress editor, not a + terminal, not a blank screen +- [ ] Going past the last slide does the same +- [ ] `--status` now shows a presentation time and a count +- [ ] Present it again: the count rises and the expiry moves out 30 days +- [ ] Unplug the network and present again: it still works + +### The awkward ones + +- [ ] A PDF and a PPTX of the same session look the same on screen +- [ ] A deliberately broken file gives a short message and only + **Remove** — no editor, no terminal +- [ ] A long session (40+ slides) opens without the library freezing +- [ ] Download a Wonder Ink worksheet: it does **not** appear in the + library + +### Remove + +Use a **test copy**. Not tomorrow's session. + +- [ ] **Remove** asks first +- [ ] The card goes, and its slides and pictures go with it +- [ ] The original download in Chrome is untouched +- [ ] It does not come back on the next scan or after a reboot + +### The manual + +- [ ] **Help** is on the bar, bottom left +- [ ] It opens with no address bar and no tabs +- [ ] It shows Browser, Presentations, sound and the common pages +- [ ] It does **not** mention Wonder Ink, Parent Call or the DVD +- [ ] It works with the network unplugged +- [ ] On a children's classroom machine it shows the opposite set +- [ ] Print `/usr/local/share/ekklesia-classroom/manual/classroom-manual.pdf` + and check the conditional wording reads sensibly in a room that + does not have every feature +- [ ] The revision date is on the printed copy + +### Wi-Fi — look, do not touch + +The laptop's Wi-Fi is already working through NetworkManager and its +ifupdown stanza is already gone. **Do not run `--adopt` on it.** + +- [ ] `classroom-wifi --check` says there is no wireless interface under + ifupdown's control +- [ ] `classroom-wifi --adopt` says "nothing to adopt" and changes nothing +- [ ] `nmcli -g 802-11-wireless.hidden connection show "ROGC Classroom"` + still reports `yes` +- [ ] The laptop still reconnects after a reboot diff --git a/scripts/classroom-manual b/scripts/classroom-manual new file mode 100755 index 0000000..e4972f4 --- /dev/null +++ b/scripts/classroom-manual @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 + +"""Open this computer's own manual. + + classroom-manual open it + classroom-manual --features say what this device would show + +One manual is installed on every machine, carrying every page. What makes +it THIS room's manual is that the pages for features this device does not +have are removed before a teacher ever sees them: a Bible study laptop +opens it and reads about the Browser, Presentations and the volume, with +no mention of Wonder Ink or Parent Call to wonder about. + +WHAT REACHES THE CLASSROOM ACCOUNT, AND WHAT DOES NOT. + +Only a list of setting NAMES — ["WONDERINK_ENABLED", ...] — and only +those the package already defines as optional applications. No values +from device.conf, no broker address, no broker password, no update URL, +no Wi-Fi anything, no other key an administrator has put in that file. + +That list is prepared by classroom-desktop-layout, which is already the +one place that decides what this device offers, and left in the runtime +directory where it is readable and carries nothing worth protecting. If +it is not there, every page is shown — which is exactly the printed +manual, and is never wrong, only less specific. + +Shown through Chrome in application mode: no address bar, no tabs, one +window that closes. Chrome is on every classroom machine already, the +manual is a local file, and none of it needs the network. +""" + +import argparse +import html +import json +import os +import subprocess +import sys +import tempfile + +sys.path.insert(0, "/usr/local/lib/ekklesia-classroom") +sys.path.insert( + 0, + os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "lib"), +) + +from classroom_config import FEATURES, feature, read_device_config # noqa: E402 + +MANUAL = "/usr/local/share/ekklesia-classroom/manual/classroom-manual.html" + +# Written by classroom-desktop-layout at boot. See the module docstring +# for what is and is not in it. +MANIFEST = "/run/ekklesia-classroom/features.json" + + +def say(message): + print(f"classroom-manual: {message}") + + +def enabled_features(): + """The optional applications this device offers, by setting name.""" + try: + with open(MANIFEST, "r", encoding="utf-8") as handle: + listed = json.load(handle) + + if isinstance(listed, list): + # Filtered against what this package knows, so a stale or + # hand-edited manifest can only ever name a real feature. + return [name for name in listed if name in FEATURES] + except (OSError, ValueError): + pass + + # No manifest. Fall back to asking device.conf directly for the + # feature keys and nothing else — the same question, one step less + # removed. A root-run --features does this; a classroom session + # normally has the manifest. + config = read_device_config() + + return [name for name in sorted(FEATURES) if feature(config, name)] + + +def page(features): + """The installed manual, with this device's features declared in it.""" + with open(MANUAL, "r", encoding="utf-8") as handle: + text = handle.read() + + declaration = ( + "" + ) + + # Before the viewer script, which reads it. Nothing else in the file + # is touched, so what a teacher reads is the file the package + # installed. + return text.replace("", "" + declaration, 1) + + +def main(): + parser = argparse.ArgumentParser( + description="Open this computer's manual.") + parser.add_argument("--features", action="store_true", + help="say what this device would show; opens nothing") + arguments = parser.parse_args() + + features = enabled_features() + + if arguments.features: + for name in sorted(FEATURES): + print(f" {name:<24} {'shown' if name in features else 'hidden'}") + + return 0 + + if not os.path.exists(MANUAL): + say(f"the manual is not installed at {MANUAL}") + return 1 + + # Composed into the teacher's own cache rather than beside the + # installed file, which is root's and read-only. + cache = os.path.join( + os.environ.get("XDG_CACHE_HOME") + or os.path.expanduser("~/.cache"), + "ekklesia-classroom") + os.makedirs(cache, exist_ok=True) + + try: + composed = os.path.join(cache, "manual.html") + + with tempfile.NamedTemporaryFile( + "w", encoding="utf-8", dir=cache, delete=False, + suffix=".html") as handle: + handle.write(page(features)) + temporary = handle.name + + os.replace(temporary, composed) + except OSError as error: + say(f"could not prepare the manual: {error}") + return 1 + + scale = "1" + + try: + scale = subprocess.run( + ["/usr/local/bin/classroom-ui-scale"], capture_output=True, + text=True, timeout=10, check=False).stdout.strip() or "1" + except (OSError, subprocess.SubprocessError): + pass + + try: + # Application mode: the manual in a window of its own, with no + # address bar for a teacher to end up somewhere else in. The + # room's ordinary browser profile is deliberately not used, so + # opening Help cannot disturb a lesson that is already open in + # Chrome. + os.execvp("google-chrome", [ + "google-chrome", + "--ozone-platform=wayland", + f"--force-device-scale-factor={scale}", + f"--user-data-dir={os.path.join(cache, 'manual-profile')}", + "--no-first-run", + "--no-default-browser-check", + "--password-store=basic", + "--hide-crash-restore-bubble", + f"--app=file://{composed}", + ]) + except OSError as error: + say(f"could not open the browser: {error}") + say(f"the manual itself is at {composed}") + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/test-classroom-manual.sh b/tests/test-classroom-manual.sh new file mode 100755 index 0000000..15659b1 --- /dev/null +++ b/tests/test-classroom-manual.sh @@ -0,0 +1,263 @@ +#!/bin/bash +# The teacher's manual: one source, two versions, and no drift. +# +# Two halves, because the manual has two failure modes and they are not +# alike. The source can be wrong — a page for an optional feature with +# no conditional sentence makes the PRINTED manual state it +# unconditionally, which is wrong in every room that does not have it. +# And the built page can be wrong — it can show a teacher a feature +# their computer does not have, or hide one it does, or carry something +# out of device.conf that the classroom account has no business seeing. +# +# The second half runs the manual's own viewer script in node against a +# stub DOM, the same way test-classroom-network.sh evaluates the polkit +# rule: it is the actual shipped code, not a description of it. +set -uo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +echo "A. the source, and the release gate" +PYTHONPATH="$ROOT/lib" python3 - "$ROOT" <<'PY' +import subprocess +import sys +from pathlib import Path + +root = Path(sys.argv[1]) +sys.path.insert(0, str(root / "lib")) + +from classroom_config import FEATURES + +passed = failed = 0 + + +def check(label, condition, detail=""): + global passed, failed + if condition: + print(f" ok {label}") + passed += 1 + else: + print(f" FAIL {label}") + if detail: + print(f" {detail}") + failed += 1 + + +source = root / "docs" / "manual" +build = source / "build" +pages = sorted(p for p in source.glob("*.md") if p.name != "README.md") + +check("there is a manual source directory", source.is_dir()) +check("with pages in it", len(pages) >= 5, str(len(pages))) + +headers = {} + +for path in pages: + text = path.read_text(encoding="utf-8") + header = {} + + if text.startswith("---\n"): + end = text.index("\n---\n", 3) + for line in text[4:end].splitlines(): + if ":" in line: + key, value = line.split(":", 1) + header[key.strip()] = value.strip() + + headers[path.name] = header + +for name, header in headers.items(): + check(f"{name} has a title", bool(header.get("title"))) + +# THE RULE THAT KEEPS THE PRINTED MANUAL TRUE. +for name, header in headers.items(): + if header.get("feature"): + check(f"{name} says when it applies, for the printed manual", + bool(header.get("printed-if")), + "without it the printed manual states an optional feature " + "as though every room had it") + check(f"{name} names a real device.conf feature", + header["feature"] in FEATURES, + f"{header.get('feature')} is not one of " + f"{', '.join(sorted(FEATURES))}") + +documented = {h.get("feature") for h in headers.values() if h.get("feature")} + +for name in FEATURES: + check(f"{name} has a manual page", name in documented, + "a room would get a button the manual never mentions") + +orders = [h.get("order") for h in headers.values()] +check("no two pages claim the same place", len(orders) == len(set(orders)), + str(sorted(orders))) + +print(" -- the builder itself") +builder = root / "tools" / "build-manual" +check("it exists and is runnable", builder.exists() + and builder.stat().st_mode & 0o111) + +result = subprocess.run([str(builder)], capture_output=True, text=True, + timeout=120) +check("it builds without complaint", result.returncode == 0, + result.stderr.strip()) +check("it produced the digital manual", + (build / "classroom-manual.html").exists()) +check("and the printable one", (build / "classroom-manual.pdf").exists()) + +pdf = (build / "classroom-manual.pdf").read_bytes() +check("the PDF is a real PDF", pdf.startswith(b"%PDF-") + and pdf.rstrip().endswith(b"%%EOF"), str(pdf[:8])) +check("with more than one page", pdf.count(b"/Type /Page ") > 1, + str(pdf.count(b"/Type /Page "))) +check("and a revision date on it, so an old printout can be spotted", + b"Revision" in pdf) + +# Every conditional sentence has to actually reach the printed manual. +for name, header in headers.items(): + if header.get("printed-if"): + words = header["printed-if"].encode("latin-1", "replace") + check(f"{name}'s conditional sentence is in the PDF", + words in pdf, header["printed-if"]) + +check("the check mode passes on what was just built", + subprocess.run([str(builder), "--check"], capture_output=True, + timeout=120).returncode == 0) + +html = (build / "classroom-manual.html").read_text(encoding="utf-8") + +for name, header in headers.items(): + if header.get("feature"): + check(f"{name} is marked in the HTML with its feature", + f'data-feature="{header["feature"]}"' in html) + +check("the digital manual needs nothing from the network", + "http://" not in html and "https://" not in html, + "it has to work in a building with no internet") + +print() +print(f"{passed} passed, {failed} failed") +raise SystemExit(1 if failed else 0) +PY + +status=$? +[[ $status -ne 0 ]] && exit $status + +echo +echo "B. what a device actually shows" + +if ! command -v node >/dev/null 2>&1; then + echo " SKIP node is not installed; the viewer script was not evaluated" + exit 0 +fi + +node - "$ROOT" <<'JS' +const fs = require("fs"); +const path = require("path"); +const root = process.argv[2]; + +let passed = 0, failed = 0; +function check(label, condition, detail) { + if (condition) { console.log(" ok " + label); passed++; } + else { console.log(" FAIL " + label); if (detail) console.log(" " + detail); failed++; } +} + +const html = fs.readFileSync( + path.join(root, "docs/manual/build/classroom-manual.html"), "utf8"); + +// The shipped viewer, pulled out of the built page and run for real. +const script = html.slice(html.lastIndexOf("")); + +// Enough DOM for it. Sections carry the feature they need, exactly as +// the builder wrote them. +function sectionsOf(text) { + const found = []; + const re = /

/g; + let m; + while ((m = re.exec(text)) !== null) { + found.push({id: m[1], feature: m[2] || null}); + } + return found; +} + +function show(features) { + const sections = sectionsOf(html).map(s => ({ + id: s.id, + removed: false, + getAttribute: function (name) { + return name === "data-feature" ? s.feature : null; + }, + remove: function () { this.removed = true; }, + querySelector: function () { return {textContent: s.id}; }, + })); + const contents = {children: [], appendChild: function (c) { this.children.push(c); }}; + const window = {CLASSROOM_FEATURES: features}; + const document = { + querySelectorAll: () => sections, + getElementById: () => contents, + createElement: () => ({}), + }; + new Function("window", "document", script)(window, document); + return sections.filter(s => !s.removed).map(s => s.id); +} + +const all = sectionsOf(html).map(s => s.id); +const optional = sectionsOf(html).filter(s => s.feature); +check("the built manual carries every page", + all.length >= 5, all.join(", ")); +check("and some of them are optional", optional.length >= 3, + optional.map(s => s.feature).join(", ")); + +console.log(" -- the Bible study laptop: Presentations, and nothing else optional"); +const laptop = show(["PRESENTATIONS_ENABLED"]); +check("the Presentations page is shown", + laptop.some(id => id.includes("presentations")), laptop.join(", ")); +check("the Browser page is shown, because every room has one", + laptop.some(id => id.includes("browser"))); +check("sound and volume are shown", laptop.some(id => id.includes("sound"))); +check("Wonder Ink is NOT shown", + !laptop.some(id => id.includes("wonderink")), laptop.join(", ")); +check("Parent Call is NOT shown", + !laptop.some(id => id.includes("parent-call")), laptop.join(", ")); +check("the DVD page is NOT shown", + !laptop.some(id => id.includes("dvd")), laptop.join(", ")); + +console.log(" -- a children's classroom: everything but Presentations"); +const classroom = show(["WONDERINK_ENABLED", "PARENT_CALL_ENABLED", "DVD_ENABLED"]); +check("Wonder Ink is shown", classroom.some(id => id.includes("wonderink"))); +check("Parent Call is shown", classroom.some(id => id.includes("parent-call"))); +check("the DVD page is shown", classroom.some(id => id.includes("dvd"))); +check("Presentations is NOT shown", + !classroom.some(id => id.includes("presentations")), classroom.join(", ")); + +console.log(" -- a device that could not say what it has"); +// No manifest: show everything, which is the printed manual and is never +// wrong, only less specific. +const unknown = show(null); +check("every page is shown rather than none", + unknown.length === all.length, unknown.length + " of " + all.length); + +console.log(" -- nothing optional at all"); +const bare = show([]); +check("only the common pages remain", + bare.length === all.length - optional.length, + bare.join(", ")); + +console.log(" -- what the page is allowed to know about the device"); +const manual = fs.readFileSync(path.join(root, "scripts/classroom-manual"), "utf8"); +check("the manual is handed a list of setting NAMES, not device.conf", + /window\.CLASSROOM_FEATURES/.test(manual) + && /json\.dumps\(sorted\(features\)\)/.test(manual)); +check("and the names are filtered against the features the package knows", + /name for name in listed if name in FEATURES/.test(manual), + "a stale manifest must not be able to name anything else"); +const layout = fs.readFileSync(path.join(root, "scripts/classroom-desktop-layout"), "utf8"); +check("the manifest is written from the enabled features alone", + /setting for variable, setting in FEATURE_LINES if answers\.get\(variable\)/.test(layout)); +check("no value from device.conf is written into it", + !/read_device_config\(\)\[/.test(layout)); +check("it lives in the runtime directory, so it cannot go stale", + /\/run\/ekklesia-classroom\/features\.json/.test(layout)); + +console.log(); +console.log(passed + " passed, " + failed + " failed"); +process.exit(failed ? 1 : 0); +JS diff --git a/tools/build-manual b/tools/build-manual new file mode 100755 index 0000000..d86fe23 --- /dev/null +++ b/tools/build-manual @@ -0,0 +1,736 @@ +#!/usr/bin/env python3 + +"""Build the teacher's manual, both of it, from docs/manual. + + tools/build-manual build everything into docs/manual/build + tools/build-manual --check verify only; what the release runs + +TWO OUTPUTS, ONE SOURCE. + + classroom-manual.html installed on every classroom computer. It + carries every page, each marked with the + device.conf setting that decides whether that + computer shows it, and hides the ones this + device does not have when it is opened. + + classroom-manual.pdf the universal printed manual. Every page, + every feature, with a conditional sentence + above the optional ones so that it stays true + in a room that does not have them all. + +They cannot drift apart because neither is written: both are generated +from the same Markdown, and --check fails the release if a page is +missing what the printed version needs. + +WHY THE PDF IS WRITTEN HERE RATHER THAN CONVERTED. + +A manual is headings, paragraphs, bullets, numbered steps and one small +table. Writing that into a PDF directly is a couple of hundred lines with +the standard fonts, no embedding and no dependency — and it means a +release can be built on any machine with Python. Converting the HTML +through LibreOffice was the alternative: it needs LibreOffice on whatever +builds the release, it lays a web page out like a web page, and its +output changes with the version installed. This does not. +""" + +import argparse +import base64 +import html +import mimetypes +import re +import sys +from datetime import date +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +SOURCE = ROOT / "docs" / "manual" +BUILD = SOURCE / "build" + +sys.path.insert(0, str(ROOT / "lib")) + +from classroom_config import FEATURES # noqa: E402 + + +# ------------------------------------------------------------ reading + +def parse(path): + """One manual page: its header, and its body as a list of blocks.""" + text = path.read_text(encoding="utf-8") + header = {} + + if text.startswith("---\n"): + end = text.index("\n---\n", 3) + for line in text[4:end].splitlines(): + if ":" in line: + key, value = line.split(":", 1) + header[key.strip()] = value.strip() + text = text[end + 5:] + + return header, blocks(text) + + +_IMAGE = re.compile(r"^!\[([^\]]*)\]\(([^)]+)\)\s*$") + + +def starts_block(line): + """Whether this line begins something that is not a paragraph. + + One test, used by every branch below AND by the paragraph loop, so + the two cannot disagree about a line. They did: the paragraph loop + stopped at any line beginning with "*", while the bullet rule needed + "* " — so a paragraph opening with a bold word consumed nothing, + advanced nothing, and the build hung. Every page whose first line is + bold hit it. + """ + return (line.startswith(("## ", "### ", "* ", "|")) + or bool(re.match(r"^\d+\. ", line)) + or bool(_IMAGE.match(line))) + + +def gather(lines, index, marker): + """A whole list, with each item's wrapped continuation lines folded in. + + A step written across two lines in the source is one step, not a step + and a paragraph. Without this the list ended at the first wrapped + item and the next marker started a NEW list — so a three-step + procedure printed as three separate steps all numbered 1, which is + the worst possible way to get an instruction wrong. + """ + items = [] + pattern = re.compile(marker) + + while index < len(lines): + line = lines[index] + + if pattern.match(line): + items.append(re.sub(marker, "", line).strip()) + index += 1 + elif items and line.strip() and line[0] in " \t": + # An indented line under an item belongs to that item. + items[-1] += " " + line.strip() + index += 1 + else: + break + + return items, index + + +def blocks(text): + """Markdown, as much of it as a teacher's manual needs. + + Headings, paragraphs, bullets, numbered steps and a simple table. + Deliberately not a Markdown library: the same text has to come out + right as a web page and as typeset print, and the small fixed set + that does is easier to keep honest than a general one. + """ + out = [] + lines = text.splitlines() + index = 0 + + while index < len(lines): + line = lines[index].rstrip() + + if not line.strip(): + index += 1 + continue + + if _IMAGE.match(line): + match = _IMAGE.match(line) + out.append(("image", (match.group(1), match.group(2)))) + index += 1 + elif line.startswith("### "): + out.append(("h3", line[4:].strip())) + index += 1 + elif line.startswith("## "): + out.append(("h2", line[3:].strip())) + index += 1 + elif line.startswith("| "): + rows = [] + while index < len(lines) and lines[index].startswith("|"): + cells = [c.strip() for c in lines[index].strip().strip("|").split("|")] + # The |---|---| rule under a header row is not data. + if not all(set(c) <= set("-: ") for c in cells): + rows.append(cells) + index += 1 + out.append(("table", rows)) + elif line.startswith("* "): + items, index = gather(lines, index, r"^\* ") + out.append(("bullets", items)) + elif re.match(r"^\d+\. ", line): + items, index = gather(lines, index, r"^\d+\. ") + out.append(("steps", items)) + else: + # Always consumes the line it was given, whatever it is, so + # this loop can never fail to advance. + paragraph = [line.strip()] + index += 1 + + while index < len(lines) and lines[index].strip() and \ + not starts_block(lines[index]): + paragraph.append(lines[index].strip()) + index += 1 + + out.append(("p", " ".join(paragraph))) + + return out + + +def pages(): + found = [] + + for path in sorted(SOURCE.glob("*.md")): + if path.name == "README.md": + continue + + header, body = parse(path) + header["path"] = path + found.append((header, body)) + + found.sort(key=lambda page: int(page[0].get("order", "99"))) + + return found + + +# ------------------------------------------------------------ marking + +_BOLD = re.compile(r"\*\*(.+?)\*\*") + + +def inline_html(text): + return _BOLD.sub(r"\1", html.escape(text)) + + +def plain(text): + """The same text with its marking removed, for the PDF.""" + return _BOLD.sub(r"\1", text) + + +# --------------------------------------------------------------- HTML + +STYLE = """ +:root { color-scheme: dark; } +* { box-sizing: border-box; } +body { + margin: 0; padding: 0 24px 80px; + background: #141c20; color: #e6edef; + font-family: "Noto Sans", "DejaVu Sans", sans-serif; + font-size: 19px; line-height: 1.65; +} +.sheet { max-width: 46rem; margin: 0 auto; } +h1 { font-size: 2.1rem; margin: 2rem 0 0.2rem; } +h2 { font-size: 1.5rem; margin: 2.4rem 0 0.4rem; + border-bottom: 1px solid #25333a; padding-bottom: .3rem; } +h3 { font-size: 1.15rem; margin: 1.6rem 0 0.3rem; color: #9fd8d1; } +p, li { margin: .5rem 0; } +ol, ul { padding-left: 1.4rem; } +strong { color: #ffffff; } +table { border-collapse: collapse; margin: 1rem 0; width: 100%; } +td, th { border: 1px solid #25333a; padding: .5rem .7rem; text-align: left; } +th { background: #0f171a; } +nav { margin: 1.5rem 0 2.5rem; padding: 1rem 1.2rem; + background: #0f171a; border: 1px solid #25333a; border-radius: 14px; } +nav a { color: #7fd0c6; text-decoration: none; display: block; padding: .2rem 0; } +nav a:hover { text-decoration: underline; } +figure { margin: 1.2rem 0; } +figure img { max-width: 100%; height: auto; display: block; + border: 1px solid #25333a; border-radius: 12px; } +figcaption { color: #93a5aa; font-size: .9rem; margin-top: .4rem; } +.revision { color: #93a5aa; font-size: .95rem; } +.top { color: #7fd0c6; text-decoration: none; font-size: .95rem; } +@media print { + body { background: #fff; color: #000; font-size: 11pt; } + nav, .top { display: none; } +} +""" + + +def embedded(source): + """A picture as a data: URI, so the manual stays ONE file. + + The installed manual is composed into the teacher's cache with this + device's features declared in it, so a relative path would point at a + directory that is not beside the copy being opened. Embedding means + the file works wherever it is put, with nothing to install alongside + it and nothing to go missing — which is also what makes it work with + the network unplugged. + """ + path = SOURCE / source + kind = mimetypes.guess_type(path.name)[0] or "image/png" + data = base64.b64encode(path.read_bytes()).decode("ascii") + + return f"data:{kind};base64,{data}" + + +def render_html(all_pages, revision): + parts = [ + "", '', "", + '', + '', + "Classroom manual", + f"", "", "", '
', + "

Classroom manual

", + f'

River of Glory · {revision}

', + "", + ] + + for header, body in all_pages: + feature = header.get("feature", "") + anchor = header["path"].stem + # The one thing the page carries into the browser: which setting + # decides whether this device shows it. No value, no other + # setting, and nothing about the machine. + attribute = f' data-feature="{html.escape(feature)}"' if feature else "" + parts.append(f'
') + parts.append(f'

{inline_html(header["title"])}

') + + for kind, content in body: + if kind == "h2": + parts.append(f"

{inline_html(content)}

") + elif kind == "h3": + parts.append(f"

{inline_html(content)}

") + elif kind == "p": + parts.append(f"

{inline_html(content)}

") + elif kind == "bullets": + parts.append("
    " + "".join( + f"
  • {inline_html(i)}
  • " for i in content) + "
") + elif kind == "steps": + parts.append("
    " + "".join( + f"
  1. {inline_html(i)}
  2. " for i in content) + "
") + elif kind == "image": + alt, source = content + parts.append( + f'
' + f'
{inline_html(alt)}
') + elif kind == "table": + rows = [] + for number, row in enumerate(content): + tag = "th" if number == 0 else "td" + rows.append("" + "".join( + f"<{tag}>{inline_html(c)}" for c in row) + + "") + parts.append("" + "".join(rows) + "
") + + parts.append('

Back to the top

') + parts.append("
") + + parts.append("
") + parts.append(f"") + parts.append("") + + return "\n".join(parts) + + +# What the manual does when a teacher opens it. +# +# classroom-manual writes the device's enabled features into +# window.CLASSROOM_FEATURES before this runs. It is a list of setting +# names and nothing else — no values from device.conf, no broker +# password, no addresses, nothing an administrator set. With no list at +# all, every page is shown, which is the printed manual's behaviour and +# is always true, just less specific. +VIEWER = """ +(function () { + var have = window.CLASSROOM_FEATURES || null; + var sections = document.querySelectorAll('section'); + var contents = document.getElementById('contents'); + + for (var i = 0; i < sections.length; i++) { + var needs = sections[i].getAttribute('data-feature'); + + if (needs && have && have.indexOf(needs) === -1) { + sections[i].remove(); + continue; + } + + var link = document.createElement('a'); + link.href = '#' + sections[i].id; + link.textContent = sections[i].querySelector('h2').textContent; + contents.appendChild(link); + } +})(); +""" + + +# ---------------------------------------------------------------- PDF + +# The 14 fonts every PDF reader has. Nothing is embedded, so the file is +# small and identical everywhere. +BODY_FONT = "Helvetica" +BOLD_FONT = "Helvetica-Bold" + +PAGE_WIDTH, PAGE_HEIGHT = 595, 842 # A4 in points +MARGIN_X, MARGIN_TOP, MARGIN_BOTTOM = 62, 780, 64 + +# Helvetica's own widths, per 1000 units, for the printable ASCII range. +# Wrapping by character count puts a line of capitals off the page and +# leaves a line of commas half empty, and this is a manual people print. +_WIDTHS = { + " ": 278, "!": 278, '"': 355, "#": 556, "$": 556, "%": 889, "&": 667, + "'": 191, "(": 333, ")": 333, "*": 389, "+": 584, ",": 278, "-": 333, + ".": 278, "/": 278, ":": 278, ";": 278, "<": 584, "=": 584, ">": 584, + "?": 556, "@": 1015, "[": 278, "\\": 278, "]": 278, "^": 469, "_": 556, + "`": 333, "{": 334, "|": 260, "}": 334, "~": 584, +} + + +def width_of(text, size, bold=False): + total = 0 + + for character in text: + if character.isdigit(): + total += 556 + elif character.isupper(): + total += 722 if bold else 667 + elif character.islower(): + total += 556 if bold else 500 + else: + total += _WIDTHS.get(character, 556) + + return total * size / 1000.0 + + +def wrap(text, size, available, bold=False): + lines = [] + current = "" + + for word in text.split(): + candidate = f"{current} {word}".strip() + + if current and width_of(candidate, size, bold) > available: + lines.append(current) + current = word + else: + current = candidate + + if current: + lines.append(current) + + return lines or [""] + + +def escape_pdf(text): + out = [] + + for character in text: + if character in "()\\": + out.append("\\" + character) + elif ord(character) < 128: + out.append(character) + else: + # The standard encoding has no place for a curly quote or a + # dash a writer typed without thinking. Replaced rather than + # dropped, so a printed line never loses a word. + out.append({ + "’": "'", "‘": "'", "“": '"', "”": '"', + "—": "-", "–": "-", " ": " ", "·": "-", + "→": "->", "←": "<-", "↑": "up", + "↓": "down", "×": "x", "⏏": "eject", + "✕": "X", "◀": "<", "▶": ">", + }.get(character, "?")) + + return "".join(out) + + +class Pdf: + """A very small PDF writer: text, in two weights, on A4 pages.""" + + def __init__(self): + self.pages = [] + self.current = [] + self.y = MARGIN_TOP + + def _show(self, text, x, y, size, bold): + font = "F2" if bold else "F1" + self.current.append( + f"BT /{font} {size} Tf 1 0 0 1 {x:.1f} {y:.1f} Tm " + f"({escape_pdf(text)}) Tj ET") + + def break_page(self): + if self.current: + self.pages.append(self.current) + + self.current = [] + self.y = MARGIN_TOP + + def space(self, amount): + self.y -= amount + + def need(self, amount): + if self.y - amount < MARGIN_BOTTOM: + self.break_page() + + def text(self, content, size=10.5, bold=False, indent=0, leading=None): + leading = leading or size * 1.42 + available = PAGE_WIDTH - 2 * MARGIN_X - indent + + for line in wrap(content, size, available, bold): + self.need(leading) + self._show(line, MARGIN_X + indent, self.y, size, bold) + self.y -= leading + + def bullet(self, mark, content, size=10.5, indent=16): + leading = size * 1.42 + available = PAGE_WIDTH - 2 * MARGIN_X - indent - 14 + lines = wrap(content, size, available) + + self.need(leading * len(lines)) + self._show(mark, MARGIN_X + indent, self.y, size, False) + + for number, line in enumerate(lines): + if number: + self.need(leading) + self._show(line, MARGIN_X + indent + 16, self.y, size, False) + self.y -= leading + + def rule(self): + self.need(10) + self.current.append( + f"0.75 w 0.6 0.6 0.6 RG {MARGIN_X} {self.y + 4:.1f} m " + f"{PAGE_WIDTH - MARGIN_X} {self.y + 4:.1f} l S") + self.y -= 8 + + def build(self): + self.break_page() + + objects = [] + count = len(self.pages) + # 1 catalog, 2 pages, 3..(2+n) page objects, then the contents, + # then the two fonts. + kids = " ".join(f"{3 + i} 0 R" for i in range(count)) + + objects.append("<< /Type /Catalog /Pages 2 0 R >>") + objects.append( + f"<< /Type /Pages /Kids [{kids}] /Count {count} >>") + + for index in range(count): + contents = 3 + count + index + objects.append( + f"<< /Type /Page /Parent 2 0 R " + f"/MediaBox [0 0 {PAGE_WIDTH} {PAGE_HEIGHT}] " + f"/Resources << /Font << /F1 {3 + 2 * count} 0 R " + f"/F2 {4 + 2 * count} 0 R >> >> " + f"/Contents {contents} 0 R >>") + + for page in self.pages: + stream = "\n".join(page) + objects.append( + f"<< /Length {len(stream.encode('latin-1', 'replace'))} >>\n" + f"stream\n{stream}\nendstream") + + for font in (BODY_FONT, BOLD_FONT): + objects.append( + f"<< /Type /Font /Subtype /Type1 /BaseFont /{font} " + f"/Encoding /WinAnsiEncoding >>") + + out = ["%PDF-1.4"] + offsets = [] + position = len(out[0]) + 1 + + for number, body in enumerate(objects, start=1): + chunk = f"{number} 0 obj\n{body}\nendobj" + offsets.append(position) + out.append(chunk) + position += len(chunk.encode("latin-1", "replace")) + 1 + + table = [f"xref\n0 {len(objects) + 1}", "0000000000 65535 f "] + table += [f"{offset:010d} 00000 n " for offset in offsets] + out.append("\n".join(table)) + out.append( + f"trailer\n<< /Size {len(objects) + 1} /Root 1 0 R >>\n" + f"startxref\n{position}\n%%EOF") + + return ("\n".join(out)).encode("latin-1", "replace") + + +def render_pdf(all_pages, revision): + pdf = Pdf() + + pdf.space(60) + pdf.text("Classroom manual", size=26, bold=True) + pdf.space(6) + pdf.text("River of Glory", size=13) + pdf.space(2) + pdf.text(revision, size=10.5) + pdf.space(18) + pdf.text( + "This manual covers every classroom computer in the church. Not " + "every room has every button, so some pages begin by saying when " + "they apply. If your computer does not have what a page " + "describes, that page is not for your room.", size=10.5) + pdf.space(10) + pdf.text( + "Keep this copy in the room. If the date above is older than the " + "one on the computer's own Help, ask the office for a new one.", + size=10.5) + pdf.break_page() + + for header, body in all_pages: + pdf.space(6) + pdf.text(plain(header["title"]), size=17, bold=True) + pdf.space(2) + pdf.rule() + pdf.space(6) + + # THE CONDITIONAL SENTENCE. + # + # This is what lets one printed manual be true in every room. A + # page for something optional says so before it says anything + # else, so a teacher in a room without it knows within one line. + if header.get("printed-if"): + pdf.text(plain(header["printed-if"]) + ".", size=10.5, bold=True) + pdf.space(6) + + for kind, content in body: + if kind == "h2": + pdf.space(8) + pdf.text(plain(content), size=12.5, bold=True) + pdf.space(3) + elif kind == "h3": + pdf.space(5) + pdf.text(plain(content), size=11, bold=True) + elif kind == "p": + pdf.text(plain(content)) + pdf.space(5) + elif kind == "bullets": + for item in content: + pdf.bullet("-", plain(item)) + pdf.space(5) + elif kind == "steps": + for number, item in enumerate(content, start=1): + pdf.bullet(f"{number}.", plain(item)) + pdf.space(5) + elif kind == "image": + pdf.text(f"[ Picture: {plain(content[0])} — see the " + "computer's own Help ]", size=9.5) + pdf.space(5) + elif kind == "table": + for number, row in enumerate(content): + pdf.bullet( + " ", " ".join(plain(c) for c in row) + if number else + " ".join(plain(c).upper() for c in row)) + pdf.space(5) + + pdf.break_page() + + return pdf.build() + + +# -------------------------------------------------------------- check + +def check(all_pages): + """Everything that would let the two versions drift apart.""" + problems = [] + + if not all_pages: + problems.append("there are no manual pages at all") + + seen = set() + + for header, body in all_pages: + name = header["path"].name + + for required in ("title", "order"): + if not header.get(required): + problems.append(f"{name}: no {required}") + + if header.get("order") in seen: + problems.append(f"{name}: another page already has order " + f"{header.get('order')}") + + seen.add(header.get("order")) + + feature = header.get("feature") + + if feature: + # A page for an optional feature MUST carry the sentence the + # printed manual puts above it, or the printed manual quietly + # becomes wrong for every room without that feature. + if not header.get("printed-if"): + problems.append( + f"{name}: declares feature {feature} but has no " + "printed-if, so the printed manual would state it " + "unconditionally") + + if feature not in FEATURES: + problems.append( + f"{name}: {feature} is not a device.conf feature " + f"(known: {', '.join(sorted(FEATURES))})") + + if not body: + problems.append(f"{name}: no content") + + for kind, content in body: + if kind != "image": + continue + + picture = SOURCE / content[1] + + if not picture.exists(): + problems.append(f"{name}: {content[1]} is not there") + elif not content[0].strip(): + # The printed manual prints the alt text as the caption, + # so a picture with none leaves a bare "[ Picture: ]". + problems.append( + f"{name}: {content[1]} has no caption, which the " + "printed manual needs") + + # Every optional feature the package has should be documented, or a + # room gets a button the manual never mentions. + documented = {h.get("feature") for h, _ in all_pages if h.get("feature")} + + for feature in FEATURES: + if feature not in documented: + problems.append( + f"{feature} is an optional feature with no manual page") + + return problems + + +def main(): + parser = argparse.ArgumentParser( + description="Build the teacher's manual, digital and printed.") + parser.add_argument("--check", action="store_true", + help="verify the source and the built files only") + arguments = parser.parse_args() + + all_pages = pages() + problems = check(all_pages) + + for problem in problems: + print(f"build-manual: {problem}", file=sys.stderr) + + if problems: + return 1 + + revision = f"Revision {date.today():%d %B %Y}" + + if arguments.check: + for name in ("classroom-manual.html", "classroom-manual.pdf"): + if not (BUILD / name).exists(): + print(f"build-manual: {name} has not been built; run " + "tools/build-manual", file=sys.stderr) + return 1 + + print(f"build-manual: {len(all_pages)} pages, " + f"{len([h for h, _ in all_pages if h.get('feature')])} of them " + "optional, and both versions are built") + return 0 + + BUILD.mkdir(parents=True, exist_ok=True) + + (BUILD / "classroom-manual.html").write_text( + render_html(all_pages, revision), encoding="utf-8") + (BUILD / "classroom-manual.pdf").write_bytes( + render_pdf(all_pages, revision)) + + print(f"build-manual: {BUILD / 'classroom-manual.html'}") + print(f"build-manual: {BUILD / 'classroom-manual.pdf'}") + print(f"build-manual: {revision}") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/build-release.sh b/tools/build-release.sh index 8743acd..cc8887e 100755 --- a/tools/build-release.sh +++ b/tools/build-release.sh @@ -52,6 +52,15 @@ done find "$ROOT/scripts" "$ROOT/lib" "$ROOT/tests" \ -name '__pycache__' -type d -prune -exec rm -rf {} + 2>/dev/null || true +# THE MANUAL IS PART OF THE RELEASE, NOT A JOB FOR AFTERWARDS. +# +# Built from docs/manual before the tests run, so that the test which +# checks the digital and printed versions against each other is checking +# what this package is about to ship rather than whatever was built last. +# A page that would make the printed manual wrong fails the build here. +echo "Building the teacher's manual" +"$ROOT/tools/build-manual" + for test in "$ROOT"/tests/test-*.sh; do [[ -x "$test" ]] || continue @@ -348,9 +357,10 @@ install -Dm644 \ "$ROOT/config/dbus/org.kde.LogoutPrompt.service" \ "$STAGE/usr/local/share/dbus-1/services/org.kde.LogoutPrompt.service" -# Two marks the classroom draws itself, because nothing in Breeze says -# either thing: a lesson, and fetching a child's grown-up. -for icon in ekklesia-wonderink ekklesia-parent-call; do +# Three marks the classroom draws itself, because nothing in Breeze says +# any of them: a lesson, fetching a child's grown-up, and tonight's +# slides. +for icon in ekklesia-wonderink ekklesia-parent-call ekklesia-presentations; do install -Dm644 \ "$ROOT/config/icons/${icon}.svg" \ "$STAGE/usr/share/icons/hicolor/scalable/apps/${icon}.svg" @@ -374,6 +384,50 @@ install -Dm755 \ "$ROOT/scripts/classroom-wonderink" \ "$STAGE/usr/local/bin/classroom-wonderink" +# PRESENTATIONS. +# +# The library a teacher sees, and the watcher that fills it from their +# downloads. Both are installed on every machine; PRESENTATIONS_ENABLED +# in device.conf is what decides whether either ever runs, and it +# defaults to false so an existing classroom upgrades unchanged. +# THE MANUAL. +# +# One HTML file carrying every page, and the command that opens it with +# the pages this device has no use for taken out. Installed on every +# machine including ones with no optional features at all, because the +# common pages — the bar, the browser, the volume, what to do when +# something is wrong — are the ones a new teacher needs most. +install -Dm644 \ + "$ROOT/docs/manual/build/classroom-manual.html" \ + "$STAGE/usr/local/share/ekklesia-classroom/manual/classroom-manual.html" + +# The printable one goes on the machine too. A room whose printed copy has +# gone missing can print another without anybody fetching a file from a +# laptop, and its revision date is the same one the digital manual shows. +install -Dm644 \ + "$ROOT/docs/manual/build/classroom-manual.pdf" \ + "$STAGE/usr/local/share/ekklesia-classroom/manual/classroom-manual.pdf" + +install -Dm755 \ + "$ROOT/scripts/classroom-manual" \ + "$STAGE/usr/local/bin/classroom-manual" + +install -Dm755 \ + "$ROOT/scripts/classroom-presentations" \ + "$STAGE/usr/local/bin/classroom-presentations" + +install -Dm755 \ + "$ROOT/scripts/classroom-presentation-import" \ + "$STAGE/usr/local/bin/classroom-presentation-import" + +install -Dm644 \ + "$ROOT/lib/classroom_presentations.py" \ + "$STAGE/usr/local/lib/ekklesia-classroom/classroom_presentations.py" + +install -Dm644 \ + "$ROOT/systemd/user/ekklesia-classroom-presentations.service" \ + "$STAGE/etc/systemd/user/ekklesia-classroom-presentations.service" + install -Dm755 \ "$ROOT/scripts/classroom-audio" \ "$STAGE/usr/local/bin/classroom-audio" @@ -482,6 +536,8 @@ install -Dm644 \ "$ROOT/lib/classroom_icons.py" \ "$STAGE/usr/local/lib/ekklesia-classroom/classroom_icons.py" +# The palette and the overlay measurements. The DVD control bar, Parent +# Call and the Presentations library all draw themselves from this. install -Dm644 \ "$ROOT/lib/classroom_style.py" \ "$STAGE/usr/local/lib/ekklesia-classroom/classroom_style.py" diff --git a/tools/validate-trixie-release.sh b/tools/validate-trixie-release.sh index 57653b6..6bb1a47 100755 --- a/tools/validate-trixie-release.sh +++ b/tools/validate-trixie-release.sh @@ -143,6 +143,181 @@ grep -q "classroom-wifi --check" /usr/local/bin/classroom-migrate PYTHONPATH=/usr/local/lib/ekklesia-classroom python3 -c \ 'import classroom_network; classroom_network.parse("")' +# THE 0.2.5 WI-FI REGRESSION, ASSERTED ON A REAL INSTALL. +# +# The prayer-room laptop's network is hidden and its name has a space in +# it. The migration dropped the hidden flag, so the connection it built +# could never associate. Checked against the module the package actually +# installed, not the one in a checkout. +PYTHONPATH=/usr/local/lib/ekklesia-classroom python3 <<'WIFI_CHECK' +import classroom_network as n + +stanza = n.wireless(n.parse( + 'allow-hotplug wlp2s0\n' + 'iface wlp2s0 inet dhcp\n' + '\twpa-ssid "ROGC Classroom"\n' + '\twpa-psk se#cret\n' + '\twpa-scan-ssid 1\n' +), lambda device: True)[0] + +found = n.profile(stanza) +assert found["ssid"] == "ROGC Classroom", found +assert found["hidden"] is True, "the hidden flag was dropped again" +assert found["psk"] == "se#cret", "a # in the password was truncated again" +assert found["key_mgmt"] == "wpa-psk", found + +# A rollback puts back exactly what it commented out, and nothing else. +before = ("auto enp1s0\niface enp1s0 inet static\n\taddress 10.40.0.9/24\n\n" + "allow-hotplug wlp2s0\niface wlp2s0 inet dhcp\n\twpa-ssid Sanctuary\n") +after = n.comment_out(before, n.wireless(n.parse(before), + lambda d: d == "wlp2s0")[0]) +restored, count = n.uncomment(after) +assert count, "nothing was marked, so a rollback would have nothing to undo" +assert restored == before, "the rollback did not restore the file exactly" +WIFI_CHECK + +# And the command itself carries both halves of the fix. +grep -q '802-11-wireless.hidden' /usr/local/bin/classroom-wifi +grep -q 'managed", "yes"' /usr/local/bin/classroom-wifi + +# PRESENTATIONS. +# +# Off by default, with everything it needs present on every machine, so +# enabling it on a device is one line in device.conf and a reboot. +command -v classroom-presentations >/dev/null +command -v classroom-presentation-import >/dev/null +command -v soffice >/dev/null +command -v pdftoppm >/dev/null +command -v pdfinfo >/dev/null +[[ -f /etc/systemd/user/ekklesia-classroom-presentations.service ]] +# Enabled the same way the audio policy is, so it actually reaches a +# session rather than merely being installed. A unit file nobody starts +# is a teacher whose download never appears. +[[ -L /etc/systemd/user/plasma-workspace.target.wants/ekklesia-classroom-presentations.service ]] +[[ -f /usr/share/applications/ekklesia-presentations.desktop ]] +[[ -f /usr/share/icons/hicolor/scalable/apps/ekklesia-presentations.svg ]] + +# A classroom that has never heard of the setting comes up without it. +PYTHONPATH=/usr/local/lib/ekklesia-classroom python3 -c \ + 'from classroom_config import feature; assert feature({}, "PRESENTATIONS_ENABLED") is False' + +# Disabled is the default, so both commands decline to run and say why. +classroom-presentations 2>&1 | grep -q 'PRESENTATIONS_ENABLED is false' +classroom-presentation-import --once 2>&1 | grep -q 'PRESENTATIONS_ENABLED is false' + +# THE CONVERSION THIS WHOLE FEATURE RESTS ON. +# +# On a real Debian, with the fonts this package installs. No window, no +# splash, no first-run dialog, no editor — measured rather than assumed, +# because "LibreOffice is installed" is not the same claim as "a PPTX +# becomes slides a teacher can be shown". +mkdir -p /tmp/deck +python3 <<'MAKE_DECK' +page = ('' + '%s') + +open("/tmp/deck/deck.fodp", "w").write( + '' + '' + '' + + (page % (1, "Wednesday Bible Study")) + + (page % (2, "Session 7")) + + '') +MAKE_DECK + +HOME=/tmp/deck timeout 240 soffice --headless --norestore --nologo \ + --convert-to pptx --outdir /tmp/deck /tmp/deck/deck.fodp >/dev/null 2>&1 +[[ -f /tmp/deck/deck.pptx ]] + +PYTHONPATH=/usr/local/lib/ekklesia-classroom HOME=/tmp/deck python3 <<'CONVERT_CHECK' +import pathlib + +import classroom_presentations as library + +ok, error = library.convert_to_pdf("/tmp/deck/deck.pptx", "/tmp/deck/slides.pdf") +assert ok, f"PPTX could not be converted: {error}" + +pages = library.page_count("/tmp/deck/slides.pdf") +assert pages == 2, f"expected 2 slides, got {pages}" + +made, error = library.render_pages("/tmp/deck/slides.pdf", "/tmp/deck/pages") +assert made == 2, f"expected 2 rendered pages, got {made}: {error}" + +# The library asks for page-001.png whatever the page count's width is. +for number in (1, 2): + path = pathlib.Path(f"/tmp/deck/pages/page-{number:03d}.png") + assert path.exists(), f"{path} was not rendered" + assert path.stat().st_size > 0, f"{path} is empty" + +# Slide 2 is the one a teacher picks as the card picture, so its text has +# to have survived the conversion. +text = library.first_text("/tmp/deck/slides.pdf", pages=2) +assert "Session 7" in text, f"text did not survive the conversion: {text!r}" +CONVERT_CHECK + +# The fonts a Google Slides export asks for, by their real names. +fc-list 2>/dev/null | grep -qi 'Liberation Sans' +fc-list 2>/dev/null | grep -qi 'Carlito' +fc-list 2>/dev/null | grep -qi 'Caladea' +# And the one that must NOT be here, because it prompts for a licence. +! dpkg -l ttf-mscorefonts-installer 2>/dev/null | grep -q '^ii' + +# THE MANUAL. +# +# Installed on every machine, works with no network at all, and shows +# only the features this device has. +[[ -f /usr/local/share/ekklesia-classroom/manual/classroom-manual.html ]] +[[ -f /usr/local/share/ekklesia-classroom/manual/classroom-manual.pdf ]] +command -v classroom-manual >/dev/null +[[ -f /usr/share/applications/ekklesia-manual.desktop ]] +head -c 5 /usr/local/share/ekklesia-classroom/manual/classroom-manual.pdf \ + | grep -q '%PDF-' +grep -aq 'Revision' /usr/local/share/ekklesia-classroom/manual/classroom-manual.pdf +! grep -qE 'https?://' \ + /usr/local/share/ekklesia-classroom/manual/classroom-manual.html + +# The manifest the manual reads carries feature NAMES and nothing else: +# no broker password, no addresses, nothing else out of device.conf. +printf 'PRESENTATIONS_ENABLED=true\nWONDERINK_ENABLED=false\nMQTT_PASSWORD=never-a-teacher\n' \ + >> /etc/ekklesia-classroom/device.conf +classroom-desktop-layout >/dev/null 2>&1 || true +[[ -f /run/ekklesia-classroom/features.json ]] +grep -q 'PRESENTATIONS_ENABLED' /run/ekklesia-classroom/features.json +! grep -q 'WONDERINK_ENABLED' /run/ekklesia-classroom/features.json +! grep -q 'never-a-teacher' /run/ekklesia-classroom/features.json +! grep -q '10.40.0.2' /run/ekklesia-classroom/features.json +classroom-manual --features | grep -qE 'PRESENTATIONS_ENABLED +shown' +classroom-manual --features | grep -qE 'WONDERINK_ENABLED +hidden' + +# The panel offers Presentations only where it is enabled, and the three +# applications that were already on the bar keep their order. +layout=/usr/share/plasma/look-and-feel/org.ekklesia.classroom.desktop/contents/layouts/org.kde.plasma.desktop-layout.js +grep -q 'var PRESENTATIONS = true;' "$layout" +grep -q 'ekklesia-presentations.desktop' "$layout" +grep -q 'ekklesia-manual.desktop' "$layout" +python3 - "$layout" <<'ORDER_CHECK' +import re +import sys + +text = open(sys.argv[1]).read() +order = re.findall(r'launchers\.push\("applications:([^"]+)"\)', text) +expected = ["ekklesia-wonderink.desktop", "google-chrome.desktop", + "ekklesia-dvd.desktop", "ekklesia-presentations.desktop"] +assert order == expected, f"the dock order changed: {order}" +ORDER_CHECK + +sed -i '/^PRESENTATIONS_ENABLED=true$/d;/^WONDERINK_ENABLED=false$/d;/^MQTT_PASSWORD=never-a-teacher$/d' \ + /etc/ekklesia-classroom/device.conf + # The policy module and the daemon agree on what they import, on a machine # rather than in a checkout. PYTHONPATH=/usr/local/lib/ekklesia-classroom python3 -c \