Reintroduce TFE duration metadata: DB columns, form fields, controllers, views, and migration

Add 'unsafe-eval' to CSP script-src directives (htmx requires Function())
This commit is contained in:
Pontoporeia
2026-06-11 13:05:37 +02:00
parent 00fed5f0e3
commit d588ae004d
81 changed files with 1061 additions and 840 deletions

View File

@@ -83,21 +83,6 @@ chown -R www-data:xamxam /var/www/xamxam/storage/cache
chmod -R 2775 /var/www/xamxam/storage/cache
ok "Cache dirs: created and owned by www-data:xamxam"
# ── Step 1b: htpasswd file ──────────────────────────────────────────────────────
printf "\n📋 Step 1b: Checking htpasswd file...\n"
echo "--------------------------------------"
if [ -f "/etc/nginx/.htpasswd-xamxam" ]; then
ok "htpasswd file exists: /etc/nginx/.htpasswd-xamxam"
elif [ -f "/etc/nginx/.htpasswd-posterg" ]; then
cp /etc/nginx/.htpasswd-posterg /etc/nginx/.htpasswd-xamxam
chmod 644 /etc/nginx/.htpasswd-xamxam
ok "Migrated .htpasswd-posterg → .htpasswd-xamxam"
else
warn "No htpasswd file found — admin panel will return 403 until one is created"
warn "Run: sudo htpasswd -c /etc/nginx/.htpasswd-xamxam <username>"
fi
# ── Step 2: Nginx config ──────────────────────────────────────────────────────
printf "\n📋 Step 2: Deploying nginx configuration...\n"
echo "--------------------------------------------"

View File

@@ -0,0 +1,95 @@
<?php
/**
* One-shot repair: fix v_theses_full / v_theses_public after migration 040
* accidentally included the already-dropped banner_path column.
*
* Run from project root: php scripts/fix-banner-path-view.php
*
* This re-executes the view portion of migration 040 (with the fix applied)
* so the views no longer reference t.banner_path.
*/
require_once __DIR__ . '/../bootstrap.php';
$db = \App\Database::getInstance();
$pdo = $db->getPdo();
echo "Dropping broken views...\n";
$pdo->exec('DROP VIEW IF EXISTS v_theses_public');
$pdo->exec('DROP VIEW IF EXISTS v_theses_full');
echo "Recreating v_theses_full (without banner_path)...\n";
$pdo->exec("
CREATE VIEW IF NOT EXISTS v_theses_full AS
SELECT
t.id,
t.identifier,
t.title,
t.subtitle,
t.year,
t.is_doctoral,
t.objet,
o.name as orientation,
ap.name as ap_program,
ft.name as finality_type,
t.synopsis,
t.context_note,
t.duration_value,
t.duration_unit,
at.name as access_type,
lt.name as license_type,
t.license_id,
t.license_custom,
t.access_type_id,
t.jury_points,
t.submitted_at,
t.defense_date,
t.published_at,
t.is_published,
t.baiu_link,
t.exemplaire_baiu,
t.exemplaire_erg,
t.cc2r,
t.remarks,
t.jury_note_added,
t.contact_visible,
GROUP_CONCAT(DISTINCT a.name ORDER BY a.name ASC) 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' AND ts.is_ulb = 0 THEN s.name END) as jury_promoteurs,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'promoteur' AND ts.is_ulb = 1 THEN s.name END) as jury_promoteurs_ulb,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' AND ts.is_external = 0 THEN s.name END) as jury_lecteurs_internes,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' AND ts.is_external = 1 THEN s.name END) as jury_lecteurs_externes,
GROUP_CONCAT(DISTINCT l.name) as languages,
GROUP_CONCAT(DISTINCT fmt.name) as formats,
GROUP_CONCAT(DISTINCT tg.name) as keywords,
-- First author's email and contact-visibility flag
(SELECT a2.email FROM authors a2 JOIN thesis_authors ta2 ON a2.id = ta2.author_id WHERE ta2.thesis_id = t.id ORDER BY ta2.author_order LIMIT 1) as author_email,
(SELECT a2.show_contact FROM authors a2 JOIN thesis_authors ta2 ON a2.id = ta2.author_id WHERE ta2.thesis_id = t.id ORDER BY ta2.author_order LIMIT 1) as author_show_contact
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_tags tt ON t.id = tt.thesis_id
LEFT JOIN tags tg ON tt.tag_id = tg.id
GROUP BY t.id;
");
echo "Recreating v_theses_public...\n";
$pdo->exec("
CREATE VIEW IF NOT EXISTS v_theses_public AS
SELECT * FROM v_theses_full
WHERE is_published = 1
");
echo "Done. Views recreated without banner_path.\n";

View File

@@ -1,199 +0,0 @@
#!/bin/bash
# Manage admin users for XAMXAM nginx basic authentication
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
PASSWORD_FILE="/etc/nginx/.htpasswd-xamxam"
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Error: This script must be run as root (use sudo)${NC}"
exit 1
fi
# Check if htpasswd is available
if ! command -v htpasswd &> /dev/null; then
echo -e "${YELLOW}Installing apache2-utils...${NC}"
apt-get update -qq
apt-get install -y apache2-utils
fi
show_menu() {
echo ""
echo -e "${BLUE}════════════════════════════════════════${NC}"
echo -e "${BLUE} XAMXAM Admin User Management${NC}"
echo -e "${BLUE}════════════════════════════════════════${NC}"
echo ""
echo "1. List all users"
echo "2. Add new user"
echo "3. Change user password"
echo "4. Delete user"
echo "5. Reset all (create new password file)"
echo "6. Exit"
echo ""
echo -n "Choose an option [1-6]: "
}
list_users() {
echo ""
if [ ! -f "$PASSWORD_FILE" ]; then
echo -e "${YELLOW}No password file found.${NC}"
return
fi
echo -e "${GREEN}Current admin users:${NC}"
echo "────────────────────────"
cut -d: -f1 "$PASSWORD_FILE" | nl
echo ""
}
add_user() {
echo ""
echo -n "Enter new username: "
read -r USERNAME
if [ -z "$USERNAME" ]; then
echo -e "${RED}Username cannot be empty${NC}"
return
fi
# Check if user already exists
if [ -f "$PASSWORD_FILE" ] && grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then
echo -e "${YELLOW}User '$USERNAME' already exists. Use option 3 to change password.${NC}"
return
fi
# Add user (use -c only if file doesn't exist)
if [ ! -f "$PASSWORD_FILE" ]; then
htpasswd -c "$PASSWORD_FILE" "$USERNAME"
else
htpasswd "$PASSWORD_FILE" "$USERNAME"
fi
echo -e "${GREEN}✓ User '$USERNAME' added successfully${NC}"
}
change_password() {
list_users
echo -n "Enter username to change password: "
read -r USERNAME
if [ -z "$USERNAME" ]; then
echo -e "${RED}Username cannot be empty${NC}"
return
fi
if [ ! -f "$PASSWORD_FILE" ]; then
echo -e "${RED}Password file not found${NC}"
return
fi
if ! grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then
echo -e "${RED}User '$USERNAME' not found${NC}"
return
fi
htpasswd "$PASSWORD_FILE" "$USERNAME"
echo -e "${GREEN}✓ Password changed for user '$USERNAME'${NC}"
}
delete_user() {
list_users
echo -n "Enter username to delete: "
read -r USERNAME
if [ -z "$USERNAME" ]; then
echo -e "${RED}Username cannot be empty${NC}"
return
fi
if [ ! -f "$PASSWORD_FILE" ]; then
echo -e "${RED}Password file not found${NC}"
return
fi
if ! grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then
echo -e "${RED}User '$USERNAME' not found${NC}"
return
fi
echo -n "Are you sure you want to delete user '$USERNAME'? [y/N] "
read -r CONFIRM
if [ "$CONFIRM" = "y" ] || [ "$CONFIRM" = "Y" ]; then
htpasswd -D "$PASSWORD_FILE" "$USERNAME"
echo -e "${GREEN}✓ User '$USERNAME' deleted${NC}"
else
echo "Cancelled"
fi
}
reset_all() {
echo ""
echo -e "${YELLOW}WARNING: This will delete ALL existing users!${NC}"
echo -n "Are you sure? [y/N] "
read -r CONFIRM
if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
echo "Cancelled"
return
fi
# Backup existing file
if [ -f "$PASSWORD_FILE" ]; then
BACKUP="${PASSWORD_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
cp "$PASSWORD_FILE" "$BACKUP"
echo -e "${GREEN}✓ Backed up to: $BACKUP${NC}"
fi
echo ""
echo -n "Enter new username: "
read -r USERNAME
if [ -z "$USERNAME" ]; then
echo -e "${RED}Username cannot be empty${NC}"
return
fi
htpasswd -c "$PASSWORD_FILE" "$USERNAME"
echo -e "${GREEN}✓ Password file reset with user '$USERNAME'${NC}"
}
# Main loop
while true; do
show_menu
read -r CHOICE
case $CHOICE in
1)
list_users
;;
2)
add_user
;;
3)
change_password
;;
4)
delete_user
;;
5)
reset_all
;;
6)
echo ""
echo "Goodbye!"
exit 0
;;
*)
echo -e "${RED}Invalid option${NC}"
;;
esac
done