Voeg 28 herkenbare kleurplaten en creatieve niveaus toe (v0.4.66-beta) #4

Open
bes-r wants to merge 207 commits from bes-r/kleurplaten-v0-4-66 into main AGit
5 changed files with 32 additions and 7 deletions
Showing only changes of commit a9bf2270d0 - Show all commits

View file

@ -1 +1 @@
0.3.55-beta
0.3.56-beta

View file

@ -206,7 +206,8 @@
+ (names.length ? ` · ${names.join(", ")}` : "")
+ (u.pending ? ` · ${T("amPending")}` : "")));
}
if(u.role!=="pupil" && can("users.role.change") && u.id!==currentUser.id){
const isStaff = ["teacher","admin","super"].includes(u.role);
if(isStaff && can("users.role.change") && u.id!==currentUser.id){
/* één functie per gebruiker: alleen de hoofdrol is te wijzigen (super);
het aparte extra-functies-systeem is uit de UI gehaald */
const rs = h("select","am-sel");
@ -221,7 +222,7 @@
/* elke staf-rol eenvoudig aan klassen koppelen (groepsleiding,
schoolbeheerder én systeemmanager): huidige klassen als chips met ,
en een select om een klas toe te voegen */
if(u.role!=="pupil" && can("classes.manage") && CLASSES.length){
if(isStaff && can("classes.manage") && CLASSES.length){
const wrap = h("div","am-classes");
(u.classIds||[]).forEach(cid=>{
const c = CLASSES.find(x=>Number(x.id)===Number(cid));

View file

@ -2,7 +2,7 @@
"use strict";
/* version shown until /api/version resolves (or if the fetch fails, e.g. offline).
Kept in sync by hand with the VERSION file at the repo root on every release. */
const VERSION = "0.3.55-beta";
const VERSION = "0.3.56-beta";
(function(){
const tag = document.getElementById("verTag");
tag.textContent = "v"+VERSION;
@ -554,7 +554,9 @@ let DATA_REV = 0;
let saveConflict = false;
let saveInFlight = false;
function canPersist(){
return !!currentUser && currentUser.role !== "pupil";
/* leerlingen en ouders slaan geen eigen borden-data op; het ouderportaal
werkt straks met gekoppelde kind-accounts i.p.v. een eigen blob */
return !!currentUser && currentUser.role !== "pupil" && currentUser.role !== "parent";
}
/* welke secties er sinds de laatste geslaagde save zijn gewijzigd; een save
verstuurt alleen die secties i.p.v. altijd de complete blob (die MB's kan

View file

@ -344,7 +344,7 @@ export default async function api(app) {
if (!c || !u) return fail(reply, 404, 'klas of gebruiker onbekend');
if (!req.user.allRoles.includes('super') && Number(c.school_id) !== Number(req.user.school_id))
return fail(reply, 403, 'geen rechten');
if (u.role === 'pupil') return fail(reply, 400, 'leerlingen horen al bij één klas');
if (!['teacher', 'admin', 'super'].includes(u.role)) return fail(reply, 400, 'alleen staf kan aan een klas gekoppeld worden');
if (u.role !== 'super' && Number(c.school_id) !== Number(u.school_id))
return fail(reply, 400, 'klas en gebruiker moeten bij dezelfde school horen');
if (remove) await pool.query('DELETE FROM class_teachers WHERE class_id = $1 AND user_id = $2', [c.id, u.id]);
@ -526,7 +526,11 @@ export default async function api(app) {
await pool.query('DELETE FROM sessions WHERE user_id = $1', [u.id]);
return { ok: true, linkCode: code };
}
if (b.role && can(req.user.allRoles, 'users.role.change') && ['super', 'admin', 'teacher'].includes(b.role) && u.role !== 'pupil')
// rolwijziging alleen tussen staf-rollen onderling: leerlingen blijven
// leerling en ouder-accounts kunnen nooit naar een beheerrol promoveren
if (b.role && can(req.user.allRoles, 'users.role.change')
&& ['super', 'admin', 'teacher'].includes(b.role)
&& ['super', 'admin', 'teacher'].includes(u.role))
await pool.query('UPDATE users SET role = $1, school_id = $2 WHERE id = $3',
[b.role, b.role === 'super' ? null : u.school_id, u.id]);
// extra functies naast de hoofdrol (bv. een teacher die ook admin-rechten krijgt) -

View file

@ -79,6 +79,24 @@ test('schoolbeheerder en systeemmanager kunnen aan een klas gekoppeld worden', a
await app4.close();
});
test('ouder-achtige rol krijgt geen promotie en geen klaskoppeling', async () => {
// rolwijziging: alleen staf → staf; een parent-account mag nooit promoveren
const { app, calls } = await makeApp(superUser, { id: 7, role: 'parent', school_id: null });
const res = await app.inject({ method: 'PATCH', url: '/api/admin/users/7', cookies,
payload: { role: 'admin' } });
assert.equal(res.statusCode, 200, res.body);
assert.ok(!calls.some((c) => c.sql.startsWith('UPDATE users SET role')),
'rolwijziging mag voor een ouder-account niet uitgevoerd worden');
await app.close();
// klaskoppeling: parent weigeren, net als pupil
const { app: app2, calls: calls2 } = await makeApp(superUser, { id: 7, role: 'parent', school_id: null });
const res2 = await app2.inject({ method: 'POST', url: '/api/admin/classes/100/teachers', cookies,
payload: { userId: 7 } });
assert.equal(res2.statusCode, 400);
assert.ok(!calls2.some((c) => c.sql.includes('INSERT INTO class_teachers')));
await app2.close();
});
test('hernoemen weigert een lege of te lange naam', async () => {
const { app } = await makeApp(superUser);
const res = await app.inject({ method: 'PATCH', url: '/api/admin/schools/5', cookies,