feat: jury composition + banner image upload

- migration 004: thesis_supervisors.role + is_external; view adds jury_president/jury_promoteurs/jury_lecteurs
- migration 005: theses.banner_path; view exposes t.banner_path and t.license_id
- Database: getThesisJury(), setThesisJury(), setBannerPath()
- admin/add.php: jury fieldset (président/promoteur/lecteurs + externe checkboxes, JS add/remove rows); banner file input
- admin/edit.php: jury fieldset pre-populated from DB; banner preview + remove checkbox + upload; multipart form
- admin/actions/formulaire.php: parse jury fields → setThesisJury(); banner upload to banners/
- tfe.php: three conditional jury rows (président·e, promoteur·ice, lecteur·ices)
- schema.sql: updated thesis_supervisors, theses, v_theses_full, v_theses_public definitions
- admin.css: fieldset, jury-row, jury-entry, btn-remove styles
This commit is contained in:
Pontoporeia
2026-03-24 13:25:23 +01:00
parent d87348c388
commit cefceb046c
12 changed files with 569 additions and 138 deletions

View File

@@ -0,0 +1,66 @@
-- Migration 004: Add jury role and is_external columns to thesis_supervisors
-- Existing rows get role = 'promoteur', is_external = 0 (no data loss)
ALTER TABLE thesis_supervisors ADD COLUMN role TEXT NOT NULL DEFAULT 'promoteur';
ALTER TABLE thesis_supervisors ADD COLUMN is_external INTEGER NOT NULL DEFAULT 0;
-- Recreate v_theses_full to include jury role columns
DROP VIEW IF EXISTS v_theses_full;
CREATE VIEW v_theses_full AS
SELECT
t.id,
t.identifier,
t.title,
t.subtitle,
t.year,
t.is_doctoral,
o.name as orientation,
ap.name as ap_program,
ft.name as finality_type,
t.synopsis,
t.context_note,
t.duration_minutes,
t.duration_pages,
t.file_size_info,
at.name as access_type,
lt.name as license_type,
t.license_id,
t.jury_points,
t.submitted_at,
t.defense_date,
t.published_at,
t.is_published,
t.baiu_link,
GROUP_CONCAT(DISTINCT a.name) as authors,
GROUP_CONCAT(DISTINCT s.name) as supervisors,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'president' THEN s.name END) as jury_president,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'promoteur' THEN s.name END) as jury_promoteurs,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' THEN s.name END) as jury_lecteurs,
GROUP_CONCAT(DISTINCT l.name) as languages,
GROUP_CONCAT(DISTINCT fmt.name) as formats,
GROUP_CONCAT(DISTINCT k.keyword) as keywords
FROM theses t
LEFT JOIN orientations o ON t.orientation_id = o.id
LEFT JOIN ap_programs ap ON t.ap_program_id = ap.id
LEFT JOIN finality_types ft ON t.finality_id = ft.id
LEFT JOIN access_types at ON t.access_type_id = at.id
LEFT JOIN license_types lt ON t.license_id = lt.id
LEFT JOIN thesis_authors ta ON t.id = ta.thesis_id
LEFT JOIN authors a ON ta.author_id = a.id
LEFT JOIN thesis_supervisors ts ON t.id = ts.thesis_id
LEFT JOIN supervisors s ON ts.supervisor_id = s.id
LEFT JOIN thesis_languages tl ON t.id = tl.thesis_id
LEFT JOIN languages l ON tl.language_id = l.id
LEFT JOIN thesis_formats tf ON t.id = tf.thesis_id
LEFT JOIN format_types fmt ON tf.format_id = fmt.id
LEFT JOIN thesis_keywords tk ON t.id = tk.thesis_id
LEFT JOIN keywords k ON tk.keyword_id = k.id
GROUP BY t.id;
-- Recreate public view
DROP VIEW IF EXISTS v_theses_public;
CREATE VIEW v_theses_public AS
SELECT * FROM v_theses_full
WHERE is_published = 1;

View File

@@ -0,0 +1,65 @@
-- Migration 005: Add banner_path column to theses for home page card thumbnails
ALTER TABLE theses ADD COLUMN banner_path TEXT;
-- Recreate v_theses_full to include banner_path
DROP VIEW IF EXISTS v_theses_full;
CREATE VIEW v_theses_full AS
SELECT
t.id,
t.identifier,
t.title,
t.subtitle,
t.year,
t.is_doctoral,
o.name as orientation,
ap.name as ap_program,
ft.name as finality_type,
t.synopsis,
t.context_note,
t.duration_minutes,
t.duration_pages,
t.file_size_info,
at.name as access_type,
lt.name as license_type,
t.license_id,
t.jury_points,
t.submitted_at,
t.defense_date,
t.published_at,
t.is_published,
t.baiu_link,
t.banner_path,
GROUP_CONCAT(DISTINCT a.name) as authors,
GROUP_CONCAT(DISTINCT s.name) as supervisors,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'president' THEN s.name END) as jury_president,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'promoteur' THEN s.name END) as jury_promoteurs,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' THEN s.name END) as jury_lecteurs,
GROUP_CONCAT(DISTINCT l.name) as languages,
GROUP_CONCAT(DISTINCT fmt.name) as formats,
GROUP_CONCAT(DISTINCT k.keyword) as keywords
FROM theses t
LEFT JOIN orientations o ON t.orientation_id = o.id
LEFT JOIN ap_programs ap ON t.ap_program_id = ap.id
LEFT JOIN finality_types ft ON t.finality_id = ft.id
LEFT JOIN access_types at ON t.access_type_id = at.id
LEFT JOIN license_types lt ON t.license_id = lt.id
LEFT JOIN thesis_authors ta ON t.id = ta.thesis_id
LEFT JOIN authors a ON ta.author_id = a.id
LEFT JOIN thesis_supervisors ts ON t.id = ts.thesis_id
LEFT JOIN supervisors s ON ts.supervisor_id = s.id
LEFT JOIN thesis_languages tl ON t.id = tl.thesis_id
LEFT JOIN languages l ON tl.language_id = l.id
LEFT JOIN thesis_formats tf ON t.id = tf.thesis_id
LEFT JOIN format_types fmt ON tf.format_id = fmt.id
LEFT JOIN thesis_keywords tk ON t.id = tk.thesis_id
LEFT JOIN keywords k ON tk.keyword_id = k.id
GROUP BY t.id;
-- Recreate public view
DROP VIEW IF EXISTS v_theses_public;
CREATE VIEW v_theses_public AS
SELECT * FROM v_theses_full
WHERE is_published = 1;

View File

@@ -191,6 +191,9 @@ CREATE TABLE IF NOT EXISTS theses (
-- External links
baiu_link TEXT, -- Link to institutional repository
-- Home page card banner (optional, landscape image)
banner_path TEXT, -- path relative to STORAGE_ROOT (e.g. "banners/abc.jpg")
-- Timestamps
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
@@ -217,11 +220,13 @@ CREATE TABLE IF NOT EXISTS thesis_authors (
FOREIGN KEY (author_id) REFERENCES authors(id) ON DELETE CASCADE
);
-- Supervisors per thesis (can have multiple promoters)
-- Supervisors per thesis (jury: president, promoteur, lecteurs)
CREATE TABLE IF NOT EXISTS thesis_supervisors (
thesis_id INTEGER NOT NULL,
supervisor_id INTEGER NOT NULL,
supervisor_order INTEGER DEFAULT 1,
role TEXT NOT NULL DEFAULT 'promoteur', -- 'president'|'promoteur'|'lecteur'
is_external INTEGER NOT NULL DEFAULT 0, -- 0 = internal, 1 = external
PRIMARY KEY (thesis_id, supervisor_id),
FOREIGN KEY (thesis_id) REFERENCES theses(id) ON DELETE CASCADE,
FOREIGN KEY (supervisor_id) REFERENCES supervisors(id) ON DELETE CASCADE
@@ -360,14 +365,19 @@ SELECT
t.file_size_info,
at.name as access_type,
lt.name as license_type,
t.license_id,
t.jury_points,
t.submitted_at,
t.defense_date,
t.published_at,
t.is_published,
t.baiu_link,
t.banner_path,
GROUP_CONCAT(DISTINCT a.name) as authors,
GROUP_CONCAT(DISTINCT s.name) as supervisors,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'president' THEN s.name END) as jury_president,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'promoteur' THEN s.name END) as jury_promoteurs,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' THEN s.name END) as jury_lecteurs,
GROUP_CONCAT(DISTINCT l.name) as languages,
GROUP_CONCAT(DISTINCT fmt.name) as formats,
GROUP_CONCAT(DISTINCT k.keyword) as keywords

Binary file not shown.