-- v0.3.55-beta: doorzoekbare afbeeldingscatalogus met meervoudige thema's, -- map-paden, eigenaarschap en instelbare opslagquota. ALTER TABLE users ADD COLUMN IF NOT EXISTS image_quota_bytes BIGINT CHECK (image_quota_bytes IS NULL OR image_quota_bytes >= -1); ALTER TABLE schools ADD COLUMN IF NOT EXISTS image_quota_bytes BIGINT CHECK (image_quota_bytes IS NULL OR image_quota_bytes >= -1); CREATE TABLE IF NOT EXISTS image_themes ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, key TEXT UNIQUE, scope TEXT NOT NULL CHECK (scope IN ('global','school','personal')), school_id BIGINT REFERENCES schools(id) ON DELETE CASCADE, owner_id BIGINT REFERENCES users(id) ON DELETE CASCADE, folder TEXT NOT NULL DEFAULT '', name_nl TEXT NOT NULL, name_en TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), CHECK ( (scope = 'global' AND school_id IS NULL) OR (scope = 'school' AND school_id IS NOT NULL AND owner_id IS NOT NULL) OR (scope = 'personal' AND owner_id IS NOT NULL) ) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_image_themes_global_name ON image_themes (lower(name_nl)) WHERE scope = 'global'; CREATE UNIQUE INDEX IF NOT EXISTS idx_image_themes_school_name ON image_themes (school_id, lower(name_nl)) WHERE scope = 'school'; CREATE UNIQUE INDEX IF NOT EXISTS idx_image_themes_personal_name ON image_themes (owner_id, lower(name_nl)) WHERE scope = 'personal'; CREATE TABLE IF NOT EXISTS image_folders ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, scope TEXT NOT NULL CHECK (scope IN ('global','school','personal')), school_id BIGINT REFERENCES schools(id) ON DELETE CASCADE, owner_id BIGINT REFERENCES users(id) ON DELETE CASCADE, path TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), CHECK ( (scope = 'global' AND school_id IS NULL) OR (scope = 'school' AND school_id IS NOT NULL AND owner_id IS NOT NULL) OR (scope = 'personal' AND owner_id IS NOT NULL) ) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_image_folders_global_path ON image_folders (path) WHERE scope = 'global'; CREATE UNIQUE INDEX IF NOT EXISTS idx_image_folders_school_path ON image_folders (school_id, path) WHERE scope = 'school'; CREATE UNIQUE INDEX IF NOT EXISTS idx_image_folders_personal_path ON image_folders (owner_id, path) WHERE scope = 'personal'; CREATE TABLE IF NOT EXISTS image_assets ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, key TEXT NOT NULL UNIQUE, scope TEXT NOT NULL CHECK (scope IN ('global','school','personal')), school_id BIGINT REFERENCES schools(id) ON DELETE CASCADE, owner_id BIGINT REFERENCES users(id) ON DELETE CASCADE, folder TEXT NOT NULL DEFAULT '', name TEXT NOT NULL, original_name TEXT, mime_type TEXT NOT NULL, size_bytes BIGINT NOT NULL DEFAULT 0 CHECK (size_bytes >= 0), storage_key TEXT UNIQUE, public_path TEXT UNIQUE, tags TEXT[] NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), CHECK ((storage_key IS NULL) <> (public_path IS NULL)), CHECK ( (scope = 'global' AND school_id IS NULL) OR (scope = 'school' AND school_id IS NOT NULL AND owner_id IS NOT NULL) OR (scope = 'personal' AND owner_id IS NOT NULL) ) ); CREATE INDEX IF NOT EXISTS idx_image_assets_owner ON image_assets(owner_id); CREATE INDEX IF NOT EXISTS idx_image_assets_school ON image_assets(school_id); CREATE INDEX IF NOT EXISTS idx_image_assets_folder ON image_assets(scope, folder); CREATE TABLE IF NOT EXISTS image_asset_themes ( asset_id BIGINT NOT NULL REFERENCES image_assets(id) ON DELETE CASCADE, theme_id BIGINT NOT NULL REFERENCES image_themes(id) ON DELETE CASCADE, PRIMARY KEY (asset_id, theme_id) ); CREATE INDEX IF NOT EXISTS idx_image_asset_themes_theme ON image_asset_themes(theme_id); INSERT INTO image_themes (key, scope, name_nl, name_en) VALUES ('builtin-money', 'global', 'Geld', 'Money'), ('builtin-coins', 'global', 'Munten', 'Coins'), ('builtin-banknotes', 'global', 'Briefgeld', 'Banknotes') ON CONFLICT (key) DO NOTHING; INSERT INTO image_folders (scope, path) VALUES ('global','Rekenen'), ('global','Rekenen/Geld'), ('global','Rekenen/Geld/Munten'), ('global','Rekenen/Geld/Briefgeld') ON CONFLICT DO NOTHING; INSERT INTO image_assets (key, scope, folder, name, mime_type, public_path, tags) VALUES ('builtin-money-coin-1', 'global', 'Rekenen/Geld/Munten', '1 cent', 'image/png', '/img/money/coin-1.png', ARRAY['geld','money','munt','coin','cent']), ('builtin-money-coin-2', 'global', 'Rekenen/Geld/Munten', '2 cent', 'image/png', '/img/money/coin-2.png', ARRAY['geld','money','munt','coin','cent']), ('builtin-money-coin-5', 'global', 'Rekenen/Geld/Munten', '5 cent', 'image/png', '/img/money/coin-5.png', ARRAY['geld','money','munt','coin','cent']), ('builtin-money-coin-10', 'global', 'Rekenen/Geld/Munten', '10 cent', 'image/png', '/img/money/coin-10.png', ARRAY['geld','money','munt','coin','cent']), ('builtin-money-coin-20', 'global', 'Rekenen/Geld/Munten', '20 cent', 'image/png', '/img/money/coin-20.png', ARRAY['geld','money','munt','coin','cent']), ('builtin-money-coin-50', 'global', 'Rekenen/Geld/Munten', '50 cent', 'image/png', '/img/money/coin-50.png', ARRAY['geld','money','munt','coin','cent']), ('builtin-money-coin-100', 'global', 'Rekenen/Geld/Munten', '1 euro', 'image/png', '/img/money/coin-100.png', ARRAY['geld','money','munt','coin','euro']), ('builtin-money-coin-200', 'global', 'Rekenen/Geld/Munten', '2 euro', 'image/png', '/img/money/coin-200.png', ARRAY['geld','money','munt','coin','euro']), ('builtin-money-note-500', 'global', 'Rekenen/Geld/Briefgeld', '5 euro', 'image/jpeg', '/img/money/note-500.jpg', ARRAY['geld','money','briefgeld','banknote','euro']), ('builtin-money-note-1000', 'global', 'Rekenen/Geld/Briefgeld', '10 euro', 'image/jpeg', '/img/money/note-1000.jpg', ARRAY['geld','money','briefgeld','banknote','euro']), ('builtin-money-note-2000', 'global', 'Rekenen/Geld/Briefgeld', '20 euro', 'image/jpeg', '/img/money/note-2000.jpg', ARRAY['geld','money','briefgeld','banknote','euro']), ('builtin-money-note-5000', 'global', 'Rekenen/Geld/Briefgeld', '50 euro', 'image/jpeg', '/img/money/note-5000.jpg', ARRAY['geld','money','briefgeld','banknote','euro']) ON CONFLICT (key) DO NOTHING; INSERT INTO image_asset_themes (asset_id, theme_id) SELECT a.id, t.id FROM image_assets a JOIN image_themes t ON t.key = 'builtin-money' WHERE a.key LIKE 'builtin-money-%' ON CONFLICT DO NOTHING; INSERT INTO image_asset_themes (asset_id, theme_id) SELECT a.id, t.id FROM image_assets a JOIN image_themes t ON t.key = CASE WHEN a.key LIKE 'builtin-money-coin-%' THEN 'builtin-coins' ELSE 'builtin-banknotes' END WHERE a.key LIKE 'builtin-money-%' ON CONFLICT DO NOTHING;