Commit Graph

25 Commits

Author SHA1 Message Date
Jakub Zych
7bf18de987 feat: the author edits a material in the app
- PUT /_bm/api/v1/materials/{id} (jwt.auth only, no token twin): every
  key of the edit body required, revision checked on the row locked FOR
  UPDATE, 409 with the student detail of the server version
- editableMaterialForCaller: 404 for what the caller cannot see, 403 for
  what they see but may not manage
- tags from the dictionary only (at most 4), http(s) urls, the body
  normalised as bm-notatka push does; course, author and publication kept
- loadMaterialDetail shared by GET /materials/{id} and the edit answers
2026-11-09 01:01:25 +01:00
Jakub Zych
237069c827 feat: revision-check material writes
- add jz_bm_materials.revision (default 1), read-only for GORM
- bump it only on content changes (models.ReviseMaterial)
- PUT /materials/{id} locks the row and answers 409 to a stale revision
- management answers carry revision and updated_at, the student detail
  carries revision
2026-11-08 23:33:34 +01:00
Jakub Zych
7cea8ab318 feat: move zjazd tags to the material's meeting
The one-way migration 20261108140000_move_zjazd_tags_to_meetings gives
every material tagged zjazd-N without a meeting meeting N (the lowest
when it has several), never overwrites a meeting_id, logs tags naming a
missing meeting or disagreeing with meeting_id at Warn, and deletes the
exact zjazd-N tags; their links go through ON DELETE CASCADE. Rollback
is a no-op: the backup is the pg_dump taken before migrate.
2026-11-08 20:29:31 +01:00
Jakub Zych
412821ec56 feat: index the text of attached PDFs
- jz_bm_material_file_texts caches the pdftotext output per file
- the River job jz.bm.extract_material_text extracts and resyncs
- the management multipart attach queues it inside its transaction
- file_texts in the document and a file snippet with its name
2026-11-08 20:11:19 +01:00
Jakub Zych
ffd19342b2 feat: keep the materials index in sync and add bm:materials-reindex
- the materials admin syncs after create and update and removes after
  delete, once the save transaction commits (UseMaterialIndex, set at Boot)
- POST and PUT /api/v1/bm/materials sync after the transaction, DELETE
  removes the document (its statement has no primary key, so no callback)
- bm:materials-reindex [--fresh] [--if-configured] ensures the index and
  syncs every material; it refuses without an engine and runs nightly
  at 04:15 with --if-configured
- TestMaterialSearchAgainstDatabase (Postgres) is written for the final
  verification phase
2026-11-08 19:50:50 +01:00
Jakub Zych
3346cb6ff6 feat: manage material visibility and shares
The management save/update body takes is_private, author_user_id and
share_user_ids (absent keeps each; a new material gets the caller as
author), and the managed material shows them. New routes PATCH
/materials/{id}/visibility, PUT /materials/{id}/shares and GET /users,
with the MCP tools bm_manage_set_material_visibility,
bm_manage_set_material_shares and bm_manage_list_users. The /bmadmin
form gets the private switch, the author picker and shared_with.
2026-11-08 11:56:14 +01:00
Jakub Zych
418f8240a4 feat: private materials with per-user shares
A material may be private: only its author and the users it is shared
with see it. Every student read (list, detail, course detail and
counters, the checklist linked material, the token twins) goes through
one visibility fragment. Existing materials stay public with no author.

GET/PUT /_bm/api/v1/materials/{id}/access let the author, a material
manager, toggle the flag and pick the users; the first change of an
author-less material makes the caller its author.
2026-11-08 11:52:47 +01:00
Jakub Zych
d7cd5baedf feat: private checklists for zjazd preparation
Per-user checklists with items that may link a course, reading, deadline,
material or own presentation deck. An item with a reading shares the
reading's read mark. Student API routes with token twins, MCP tools,
migrations, tests and README.
2026-10-30 12:41:04 +01:00
ce6ac0a78e test: expect signed one-time material uploads
- TestUploadTicket: HMAC under the jz.bm.upload.v1: label, expiry,
  malformed and foreign tickets, single use through the cache
- TestSignedMaterialUploadRefusesBadTickets, TestCreateMaterialUploadNeedsCaller
- TestRoutes, TestBMMCP and TestManageBucketKey expect the upload-url
  route, the uploads route with its two buckets and the upload tool
- compile-only stubs
2026-10-28 15:07:30 +01:00
93066c63ea feat: attach material files over MCP as base64
- storeMaterialFile: the one attach.Store call of the management API,
  shared by the multipart route and the new inline route
- POST /api/v1/bm/materials/{id}/files/inline: JSON {name, mime?,
  content_base64}, at most 3000000 bytes decoded, body.limit 4300000
- bm_manage_attach_material_file and bm_manage_detach_material_file,
  both warning that material files are public to anyone with the link
2026-10-28 15:03:43 +01:00
e106b36a9a feat: add the BM student MCP tools
- token twins of the student API under /_bm/api/v1/token (user.api_token,
  per-user jz.bm.token bucket, a scope on each), from one mountStudent
- pact.HasMCP with the eleven student tools over the twins
- TestBMMCP and TestTokenMirrorRoutes
2026-10-28 14:32:03 +01:00
Jakub Zych
3241cc9894 feat: zjazd (meeting) on readings and materials
The Pulpit hero lists what to read for the current or next zjazd, so
readings and materials get a nullable meeting_id (FK to jz_bm_meetings,
ON DELETE SET NULL, indexed). The reading admin form gets a "Na zjazd"
picker, the student reading JSON carries meeting_id, and the management
/readings records filter and validate it.
2026-10-27 01:41:48 +01:00
07d67be1e7 test: cover the chat access policy and bootstrap guards
ChatAccess admits only chat-group members to context bm/1 and manages
nothing; the chat context oversees nothing and its loader knows only
id 1; /chat and /chat/members need a caller and a database. The
migration count rises to 15 for the chat group seed.
2026-10-26 15:42:53 +01:00
cf84f1d0e8 feat: per-student owned flag on readings
- new table jz_bm_reading_ownership and its migration
  20261026120000_create_jz_bm_reading_ownership
- PUT and DELETE /_bm/api/v1/readings/{id}/owned, idempotent
- owned_at on every reading in /readings and /courses/{id}
- read semantics, progress routes and counts unchanged
2026-10-26 11:11:22 +01:00
Jakub Zych
2536bcb959 Management API: record routes for courses, meetings, class sessions, readings and deadlines (Fillable + Rules + TemporalErrors, PATCH, filters) 2026-10-17 18:05:39 +02:00
Jakub Zych
996b4d1542 Management API: POST/DELETE /materials/{id}/files (attach.Store, 50 MB, 20 files), DELETE /materials/{id} removes files; download keeps the file name 2026-10-17 17:53:22 +02:00
Jakub Zych
ebd9117f8c feat: lecturer on class sessions and in the session course ref
The schedule showed a lecturer only where the session notes named one
(Globalizacja), so the student plan had nobody under the other courses.
The session JSON now carries the course lecturer in its course ref and an
optional per-session lecturer (new nullable column, admin field, seed key)
for courses shared between two people. The student app shows the
session's own lecturer or, failing that, the course's.
2026-10-17 11:20:11 +02:00
Jakub Zych
d2096e2128 feat(manage): GET and PUT /api/v1/bm/materials/{id}
The management API could only upsert a material on its title and course,
so a renamed note became a second material. PUT /materials/{id} applies the
same body to one material (422 when the new title and course belong to
another one), GET /materials/{id} shows a draft or published material with
its public files, and the upsert, update and validation share one path.
The README documents the management API.
2026-10-16 20:40:47 +02:00
Jakub Zych
9986df32a4 test(02-01): add failing test for the presentations prefix
- /_presentations must be a plain 404, not the SPA shell
- A router without the presentations plugin has no public document route
2026-10-06 18:05:09 +02:00
Jakub Zych
840935d22d refactor: drop the quiz import and the golem15.quizzes dependency
golem15.quizzes now serves its own import at /api/v1/quizzes/import behind
the same shared API token.
2026-10-04 23:15:01 +02:00
Jakub Zych
97000dcd1e feat: add the management API behind the shared golem15.user API token
Serve /api/v1/bm (seed, quiz import, materials, courses, me) behind
user.api_token, user.scope and user.permission. jz.bm now requires
golem15.quizzes for the quiz import.
2026-10-04 23:11:21 +02:00
Jakub Zych
71b848a376 Adopt SummerCMS v0.1.1 admin features
Datepickers on native time.Time / lagoon.Date fields, file uploads on
materials, readings and class sessions managed from the course form
(course_id made nullable).
2026-10-03 06:03:34 +02:00
Jakub Zych
b39f627fe3 test: cover the ICS writer and the calendar feed routes 2026-10-02 17:38:48 +02:00
Jakub Zych
21107e9804 feat: serve the student app from the plugin 2026-10-02 14:43:02 +02:00
Jakub Zych
ce901d5f26 feat: add the bm:seed command
Validates a whole JSON file (course keys, meeting numbers, values, dates)
and upserts meetings, courses, class sessions, deadlines and readings in
one transaction, matched on their natural identity. Prints a created,
updated and unchanged summary; --dry-run only validates. Ships a small
example fixture with made-up data.
2026-10-02 14:26:43 +02:00