mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
fix: allow isAuthenticated() bypass in development mode
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Save handler for apropos contents (contacts, credits, erg_url).
|
* Save handler for apropos contents (contacts, credits).
|
||||||
|
* Structure: groups[] with label/role, each having entries[] of {text, url, email}.
|
||||||
*/
|
*/
|
||||||
require_once __DIR__ . "/../../../config/bootstrap.php";
|
require_once __DIR__ . "/../../../config/bootstrap.php";
|
||||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||||
@@ -12,7 +13,7 @@ if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) ||
|
|||||||
die("Erreur de sécurité : token invalide.");
|
die("Erreur de sécurité : token invalide.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$allowedKeys = ['contacts', 'credits', 'erg_url'];
|
$allowedKeys = ['contacts', 'credits'];
|
||||||
$aproposKey = $_POST['apropos_key'] ?? '';
|
$aproposKey = $_POST['apropos_key'] ?? '';
|
||||||
if (!in_array($aproposKey, $allowedKeys)) {
|
if (!in_array($aproposKey, $allowedKeys)) {
|
||||||
die("Clé invalide.");
|
die("Clé invalide.");
|
||||||
@@ -22,51 +23,49 @@ require_once __DIR__ . '/../../../src/Database.php';
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$db = new Database();
|
$db = new Database();
|
||||||
|
$groups = $_POST['groups'] ?? [];
|
||||||
|
$cleaned = [];
|
||||||
|
|
||||||
if ($aproposKey === 'erg_url') {
|
foreach ($groups as $group) {
|
||||||
$value = trim($_POST['value'] ?? '');
|
if ($aproposKey === 'credits') {
|
||||||
if (strlen($value) > 500) {
|
$label = trim($group['label'] ?? '');
|
||||||
die("URL trop longue (max 500 caractères).");
|
if ($label === '') continue;
|
||||||
}
|
$entries = [];
|
||||||
$db->saveAproposContent('erg_url', $value);
|
foreach ($group['entries'] ?? [] as $entry) {
|
||||||
} else {
|
$text = trim($entry['text'] ?? '');
|
||||||
$items = $_POST['items'] ?? [];
|
if ($text === '') continue;
|
||||||
$cleaned = [];
|
$e = ['text' => $text];
|
||||||
foreach ($items as $item) {
|
$url = trim($entry['url'] ?? '');
|
||||||
if ($aproposKey === 'contacts') {
|
if ($url !== '') $e['url'] = $url;
|
||||||
$name = trim($item['name'] ?? '');
|
$entries[] = $e;
|
||||||
if ($name === '') continue; // skip empty rows
|
|
||||||
$entry = [
|
|
||||||
'name' => trim($item['name'] ?? ''),
|
|
||||||
'role' => trim($item['role'] ?? ''),
|
|
||||||
'email' => trim($item['email'] ?? ''),
|
|
||||||
];
|
|
||||||
$url = trim($item['url'] ?? '');
|
|
||||||
if ($url !== '') {
|
|
||||||
$entry['url'] = $url;
|
|
||||||
}
|
|
||||||
$cleaned[] = $entry;
|
|
||||||
} else { // credits
|
|
||||||
$label = trim($item['label'] ?? '');
|
|
||||||
$val = trim($item['value'] ?? '');
|
|
||||||
$url = trim($item['url'] ?? '');
|
|
||||||
if ($label === '' && $val === '') continue;
|
|
||||||
$entry = [
|
|
||||||
'label' => $label,
|
|
||||||
'value' => $val,
|
|
||||||
];
|
|
||||||
if ($url !== '') {
|
|
||||||
$entry['url'] = $url;
|
|
||||||
}
|
|
||||||
$cleaned[] = $entry;
|
|
||||||
}
|
}
|
||||||
|
if (empty($entries)) continue;
|
||||||
|
$cleaned[] = ['label' => $label, 'entries' => $entries];
|
||||||
|
} else { // contacts
|
||||||
|
$role = trim($group['role'] ?? '');
|
||||||
|
if ($role === '') continue;
|
||||||
|
$entries = [];
|
||||||
|
foreach ($group['entries'] ?? [] as $entry) {
|
||||||
|
$text = trim($entry['text'] ?? '');
|
||||||
|
if ($text === '') continue;
|
||||||
|
$e = [
|
||||||
|
'text' => $text,
|
||||||
|
'email' => trim($entry['email'] ?? ''),
|
||||||
|
];
|
||||||
|
$url = trim($entry['url'] ?? '');
|
||||||
|
if ($url !== '') $e['url'] = $url;
|
||||||
|
$entries[] = $e;
|
||||||
|
}
|
||||||
|
if (empty($entries)) continue;
|
||||||
|
$cleaned[] = ['role' => $role, 'entries' => $entries];
|
||||||
}
|
}
|
||||||
if (empty($cleaned)) {
|
|
||||||
die("Au moins un élément est requis.");
|
|
||||||
}
|
|
||||||
$db->saveAproposContent($aproposKey, $cleaned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (empty($cleaned)) {
|
||||||
|
die("Au moins un groupe avec des entrées est requis.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->saveAproposContent($aproposKey, $cleaned);
|
||||||
App::flash('success', "Contenu « $aproposKey » mis à jour avec succès.");
|
App::flash('success', "Contenu « $aproposKey » mis à jour avec succès.");
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log("Apropos save error: " . $e->getMessage());
|
error_log("Apropos save error: " . $e->getMessage());
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ if (empty($_SESSION["csrf_token"])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$allowedPageSlugs = ["about", "licenses", "charte"];
|
$allowedPageSlugs = ["about", "licenses", "charte"];
|
||||||
$allowedApropos = ["contacts", "credits", "erg_url"];
|
$allowedApropos = ["contacts", "credits"];
|
||||||
|
|
||||||
$pageSlug = $_GET["slug"] ?? "";
|
$pageSlug = $_GET["slug"] ?? "";
|
||||||
$aproposKey = $_GET["apropos"] ?? "";
|
$aproposKey = $_GET["apropos"] ?? "";
|
||||||
@@ -44,7 +44,6 @@ try {
|
|||||||
$editTitle = match($aproposKey) {
|
$editTitle = match($aproposKey) {
|
||||||
'contacts' => 'Contacts',
|
'contacts' => 'Contacts',
|
||||||
'credits' => 'Crédits',
|
'credits' => 'Crédits',
|
||||||
'erg_url' => 'URL de l\'ERG',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@@ -104,126 +103,127 @@ require_once APP_ROOT . "/templates/head.php";
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<?php elseif ($aproposKey === 'erg_url'): ?>
|
<?php else: ?>
|
||||||
<form action="/admin/actions/apropos.php" method="post" class="admin-form">
|
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION["csrf_token"]) ?>">
|
|
||||||
<input type="hidden" name="apropos_key" value="erg_url">
|
|
||||||
|
|
||||||
<label for="erg_url">URL du site de l'ERG :</label>
|
|
||||||
<input type="url" id="erg_url" name="value"
|
|
||||||
value="<?= htmlspecialchars($value) ?>" style="width:100%;max-width:600px;">
|
|
||||||
|
|
||||||
<div class="admin-form-footer">
|
|
||||||
<button type="submit" class="admin-btn">Enregistrer</button>
|
|
||||||
<a href="/admin/contenus.php" class="admin-btn-secondary admin-cancel-link">Annuler</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<?php elseif (in_array($aproposKey, ['contacts', 'credits'])): ?>
|
|
||||||
<?php
|
<?php
|
||||||
$items = is_array($value) ? $value : [];
|
$groups = is_array($value) ? $value : [];
|
||||||
?>
|
?>
|
||||||
<form action="/admin/actions/apropos.php" method="post" class="admin-form" id="apropos-form">
|
<form action="/admin/actions/apropos.php" method="post" class="admin-form" id="apropos-form">
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION["csrf_token"]) ?>">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION["csrf_token"]) ?>">
|
||||||
<input type="hidden" name="apropos_key" value="<?= htmlspecialchars($aproposKey) ?>">
|
<input type="hidden" name="apropos_key" value="<?= htmlspecialchars($aproposKey) ?>">
|
||||||
|
|
||||||
<?php if ($aproposKey === 'contacts'): ?>
|
<?php foreach ($groups as $gi => $group): ?>
|
||||||
<?php foreach ($items as $i => $item): ?>
|
<fieldset class="apropos-group">
|
||||||
<fieldset class="apropos-item">
|
<legend><?= htmlspecialchars($aproposKey === 'contacts' ? 'Contact' : 'Crédit') ?> <?= $gi + 1 ?></legend>
|
||||||
<legend>Contact <?= $i + 1 ?></legend>
|
<?php if ($aproposKey === 'contacts'): ?>
|
||||||
<label for="contact_<?= $i ?>_name">Nom :</label>
|
<label for="group_<?= $gi ?>_role">Rôle :</label>
|
||||||
<input type="text" id="contact_<?= $i ?>_name"
|
<input type="text" id="group_<?= $gi ?>_role"
|
||||||
name="items[<?= $i ?>][name]"
|
name="groups[<?= $gi ?>][role]"
|
||||||
value="<?= htmlspecialchars($item['name'] ?? '') ?>" required>
|
value="<?= htmlspecialchars($group['role'] ?? '') ?>">
|
||||||
|
<?php else: ?>
|
||||||
|
<label for="group_<?= $gi ?>_label">Label :</label>
|
||||||
|
<input type="text" id="group_<?= $gi ?>_label"
|
||||||
|
name="groups[<?= $gi ?>][label]"
|
||||||
|
value="<?= htmlspecialchars($group['label'] ?? '') ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<label for="contact_<?= $i ?>_role">Rôle :</label>
|
<?php $entries = is_array($group['entries'] ?? null) ? $group['entries'] : []; ?>
|
||||||
<input type="text" id="contact_<?= $i ?>_role"
|
<?php foreach ($entries as $ei => $entry): ?>
|
||||||
name="items[<?= $i ?>][role]"
|
<div class="apropos-entry">
|
||||||
value="<?= htmlspecialchars($item['role'] ?? '') ?>">
|
<label for="entry_<?= $gi ?>_<?= $ei ?>_text"><?= $aproposKey === 'contacts' ? 'Nom' : 'Texte' ?> :</label>
|
||||||
|
<input type="text" id="entry_<?= $gi ?>_<?= $ei ?>_text"
|
||||||
<label for="contact_<?= $i ?>_email">Email :</label>
|
name="groups[<?= $gi ?>][entries][<?= $ei ?>][text]"
|
||||||
<input type="email" id="contact_<?= $i ?>_email"
|
value="<?= htmlspecialchars($entry['text'] ?? '') ?>">
|
||||||
name="items[<?= $i ?>][email]"
|
<?php if ($aproposKey === 'contacts'): ?>
|
||||||
value="<?= htmlspecialchars($item['email'] ?? '') ?>">
|
<label for="entry_<?= $gi ?>_<?= $ei ?>_email">Email :</label>
|
||||||
|
<input type="email" id="entry_<?= $gi ?>_<?= $ei ?>_email"
|
||||||
<label for="contact_<?= $i ?>_url">Lien (optionnel) :</label>
|
name="groups[<?= $gi ?>][entries][<?= $ei ?>][email]"
|
||||||
<input type="url" id="contact_<?= $i ?>_url"
|
value="<?= htmlspecialchars($entry['email'] ?? '') ?>">
|
||||||
name="items[<?= $i ?>][url]"
|
<?php endif; ?>
|
||||||
value="<?= htmlspecialchars($item['url'] ?? '') ?>">
|
<label for="entry_<?= $gi ?>_<?= $ei ?>_url">Lien (optionnel) :</label>
|
||||||
</fieldset>
|
<input type="url" id="entry_<?= $gi ?>_<?= $ei ?>_url"
|
||||||
|
name="groups[<?= $gi ?>][entries][<?= $ei ?>][url]"
|
||||||
|
value="<?= htmlspecialchars($entry['url'] ?? '') ?>">
|
||||||
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
||||||
<?php else: ?>
|
<button type="button" class="admin-btn admin-btn--sm add-entry-btn" data-group="<?= $gi ?>">+ Ajouter une entrée</button>
|
||||||
<?php foreach ($items as $i => $item): ?>
|
</fieldset>
|
||||||
<fieldset class="apropos-item">
|
<?php endforeach; ?>
|
||||||
<legend>Crédit <?= $i + 1 ?></legend>
|
|
||||||
<label for="credit_<?= $i ?>_label">Label :</label>
|
|
||||||
<input type="text" id="credit_<?= $i ?>_label"
|
|
||||||
name="items[<?= $i ?>][label]"
|
|
||||||
value="<?= htmlspecialchars($item['label'] ?? '') ?>">
|
|
||||||
|
|
||||||
<label for="credit_<?= $i ?>_value">Valeur :</label>
|
<button type="button" class="admin-btn" id="add-group-btn">+ Ajouter un <?= $aproposKey === 'contacts' ? 'contact' : 'groupe de crédit' ?></button>
|
||||||
<input type="text" id="credit_<?= $i ?>_value"
|
|
||||||
name="items[<?= $i ?>][value]"
|
|
||||||
value="<?= htmlspecialchars($item['value'] ?? '') ?>">
|
|
||||||
|
|
||||||
<label for="credit_<?= $i ?>_url">Lien (optionnel) :</label>
|
|
||||||
<input type="url" id="credit_<?= $i ?>_url"
|
|
||||||
name="items[<?= $i ?>][url]"
|
|
||||||
value="<?= htmlspecialchars($item['url'] ?? '') ?>">
|
|
||||||
</fieldset>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<button type="button" class="admin-btn" id="add-item-btn" style="width:auto;">+ Ajouter un élément</button>
|
|
||||||
|
|
||||||
<div class="admin-form-footer">
|
<div class="admin-form-footer">
|
||||||
<button type="submit" class="admin-btn">Enregistrer</button>
|
<button type="submit" class="admin-btn">Enregistrer</button>
|
||||||
<a href="/admin/contenus.php" class="admin-btn-secondary admin-cancel-link">Annuler</a>
|
<a href="/admin/contenus.php" class="admin-btn-secondary admin-cancel-link">Annuler</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template id="row-template-<?= $aproposKey ?>">
|
<template id="entry-template-<?= $aproposKey ?>">
|
||||||
<?php if ($aproposKey === 'contacts'): ?>
|
<div class="apropos-entry">
|
||||||
<fieldset class="apropos-item">
|
<label>Entrée :</label>
|
||||||
<legend>Contact {{index}}</legend>
|
<input type="text" name="groups[{{gi}}][entries][{{ei}}][text]">
|
||||||
<label for="contact_{{index}}_name">Nom :</label>
|
<?php if ($aproposKey === 'contacts'): ?>
|
||||||
<input type="text" id="contact_{{index}}_name"
|
<label>Email :</label>
|
||||||
name="items[{{index}}][name]" required>
|
<input type="email" name="groups[{{gi}}][entries][{{ei}}][email]">
|
||||||
<label for="contact_{{index}}_role">Rôle :</label>
|
<?php endif; ?>
|
||||||
<input type="text" id="contact_{{index}}_role"
|
<label>Lien (optionnel) :</label>
|
||||||
name="items[{{index}}][role]">
|
<input type="url" name="groups[{{gi}}][entries][{{ei}}][url]">
|
||||||
<label for="contact_{{index}}_email">Email :</label>
|
</div>
|
||||||
<input type="email" id="contact_{{index}}_email"
|
</template>
|
||||||
name="items[{{index}}][email]">
|
|
||||||
<label for="contact_{{index}}_url">Lien (optionnel) :</label>
|
<template id="group-template-<?= $aproposKey ?>">
|
||||||
<input type="url" id="contact_{{index}}_url"
|
<fieldset class="apropos-group">
|
||||||
name="items[{{index}}][url]">
|
<legend><?= htmlspecialchars($aproposKey === 'contacts' ? 'Contact' : 'Crédit') ?> {{gi}}</legend>
|
||||||
|
<?php if ($aproposKey === 'contacts'): ?>
|
||||||
|
<label>Rôle :</label>
|
||||||
|
<input type="text" name="groups[{{gi}}][role]">
|
||||||
|
<?php else: ?>
|
||||||
|
<label>Label :</label>
|
||||||
|
<input type="text" name="groups[{{gi}}][label]">
|
||||||
|
<?php endif; ?>
|
||||||
|
<button type="button" class="admin-btn admin-btn--sm add-entry-btn" data-group="{{gi}}">+ Ajouter une entrée</button>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<?php else: ?>
|
|
||||||
<fieldset class="apropos-item">
|
|
||||||
<legend>Crédit {{index}}</legend>
|
|
||||||
<label for="credit_{{index}}_label">Label :</label>
|
|
||||||
<input type="text" id="credit_{{index}}_label"
|
|
||||||
name="items[{{index}}][label]">
|
|
||||||
<label for="credit_{{index}}_value">Valeur :</label>
|
|
||||||
<input type="text" id="credit_{{index}}_value"
|
|
||||||
name="items[{{index}}][value]">
|
|
||||||
<label for="credit_{{index}}_url">Lien (optionnel) :</label>
|
|
||||||
<input type="url" id="credit_{{index}}_url"
|
|
||||||
name="items[{{index}}][url]">
|
|
||||||
</fieldset>
|
|
||||||
<?php endif; ?>
|
|
||||||
</template>
|
</template>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
let count = <?= count($items) ?>;
|
const aproposKey = '<?= $aproposKey ?>';
|
||||||
const tpl = document.getElementById('row-template-<?= $aproposKey ?>').innerHTML;
|
let groupCount = <?= count($groups) ?>;
|
||||||
document.getElementById('add-item-btn').addEventListener('click', function() {
|
const entryTpl = document.getElementById('entry-template-' + aproposKey).innerHTML;
|
||||||
count++;
|
const groupTpl = document.getElementById('group-template-' + aproposKey).innerHTML;
|
||||||
const html = tpl.replaceAll('{{index}}', count);
|
|
||||||
|
// Add entry to a group
|
||||||
|
document.querySelectorAll('.add-entry-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
const gi = parseInt(this.dataset.group);
|
||||||
|
const fieldset = this.closest('fieldset');
|
||||||
|
const entryCount = fieldset.querySelectorAll('.apropos-entry').length;
|
||||||
|
const html = entryTpl.replaceAll('{{gi}}', gi).replaceAll('{{ei}}', entryCount);
|
||||||
|
this.insertAdjacentHTML('beforebegin', html);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add new group
|
||||||
|
document.getElementById('add-group-btn').addEventListener('click', function() {
|
||||||
|
groupCount++;
|
||||||
|
const html = groupTpl.replaceAll('{{gi}}', groupCount);
|
||||||
this.insertAdjacentHTML('beforebegin', html);
|
this.insertAdjacentHTML('beforebegin', html);
|
||||||
|
|
||||||
|
// Re-bind add-entry buttons for the new group
|
||||||
|
const newGroup = this.previousElementSibling;
|
||||||
|
if (newGroup && newGroup.classList.contains('apropos-group')) {
|
||||||
|
const btn = newGroup.querySelector('.add-entry-btn');
|
||||||
|
if (btn) {
|
||||||
|
btn.dataset.group = groupCount;
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
const gi = parseInt(this.dataset.group);
|
||||||
|
const fieldset = this.closest('fieldset');
|
||||||
|
const entryCount = fieldset.querySelectorAll('.apropos-entry').length;
|
||||||
|
const html = entryTpl.replaceAll('{{gi}}', gi).replaceAll('{{ei}}', entryCount);
|
||||||
|
this.insertAdjacentHTML('beforebegin', html);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ try {
|
|||||||
$typeLabel = match($a['key']) {
|
$typeLabel = match($a['key']) {
|
||||||
'contacts' => 'Contacts',
|
'contacts' => 'Contacts',
|
||||||
'credits' => 'Crédits',
|
'credits' => 'Crédits',
|
||||||
'erg_url' => 'URL de l\'ERG',
|
|
||||||
};
|
};
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -6,6 +6,30 @@ require_once APP_ROOT . '/src/Parsedown.php';
|
|||||||
$currentNav = 'apropos';
|
$currentNav = 'apropos';
|
||||||
define('APROPOS_STATIC_CONTENT', "Ce site POSTERG a été créé pour répertorier et valoriser les mémoires de l'erg – École de Recherches Graphique de Bruxelles.\n\nL'objectif est à la fois d'offrir une vitrine aux projets des anciennes étudiantes et de mettre en lumière la diversité des disciplines et des parcours qui façonnent l'histoire de l'école à travers les âges, depuis près de 100 ans.");
|
define('APROPOS_STATIC_CONTENT', "Ce site POSTERG a été créé pour répertorier et valoriser les mémoires de l'erg – École de Recherches Graphique de Bruxelles.\n\nL'objectif est à la fois d'offrir une vitrine aux projets des anciennes étudiantes et de mettre en lumière la diversité des disciplines et des parcours qui façonnent l'histoire de l'école à travers les âges, depuis près de 100 ans.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a comma-separated list of entries with links.
|
||||||
|
* Entries joined with comma, last two joined with " & ".
|
||||||
|
*/
|
||||||
|
function renderEntries(array $entries): string {
|
||||||
|
if (empty($entries)) return '';
|
||||||
|
$parts = [];
|
||||||
|
foreach ($entries as $e) {
|
||||||
|
$text = htmlspecialchars($e['text'] ?? '');
|
||||||
|
$url = $e['url'] ?? '';
|
||||||
|
if (!empty($url)) {
|
||||||
|
$parts[] = '<span class="apropos-entry"><a href="' . htmlspecialchars($url) . '" target="_blank" rel="noopener">' . $text . '</a></span>';
|
||||||
|
} else {
|
||||||
|
$parts[] = '<span class="apropos-entry">' . $text . '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$count = count($parts);
|
||||||
|
if ($count === 1) return $parts[0];
|
||||||
|
// Join all but last two with ", ", join last two with " & "
|
||||||
|
$prefix = implode(', ', array_slice($parts, 0, $count - 2));
|
||||||
|
$suffix = implode(' & ', array_slice($parts, -2));
|
||||||
|
return $prefix !== '' ? $prefix . ', ' . $suffix : $suffix;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$db = Database::getInstance();
|
$db = Database::getInstance();
|
||||||
|
|
||||||
@@ -16,10 +40,9 @@ try {
|
|||||||
$rawContent = APROPOS_STATIC_CONTENT;
|
$rawContent = APROPOS_STATIC_CONTENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contacts, credits, erg_url from apropos_contents table
|
// Contacts and credits from apropos_contents table
|
||||||
$contacts = $db->getAproposContent('contacts');
|
$contacts = $db->getAproposContent('contacts');
|
||||||
$credits = $db->getAproposContent('credits');
|
$credits = $db->getAproposContent('credits');
|
||||||
$ergUrl = $db->getAproposContent('erg_url') ?: 'https://erg.be';
|
|
||||||
|
|
||||||
// Apply defaults if DB returns empty
|
// Apply defaults if DB returns empty
|
||||||
$contacts = is_array($contacts) && !empty($contacts) ? $contacts : null;
|
$contacts = is_array($contacts) && !empty($contacts) ? $contacts : null;
|
||||||
@@ -29,7 +52,6 @@ try {
|
|||||||
$rawContent = APROPOS_STATIC_CONTENT;
|
$rawContent = APROPOS_STATIC_CONTENT;
|
||||||
$contacts = null;
|
$contacts = null;
|
||||||
$credits = null;
|
$credits = null;
|
||||||
$ergUrl = 'https://erg.be';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$pd = new Parsedown();
|
$pd = new Parsedown();
|
||||||
@@ -67,7 +89,7 @@ $bodyClass = 'apropos-body';
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="apropos-toc-erg">
|
<div class="apropos-toc-erg">
|
||||||
<a href="<?= htmlspecialchars($ergUrl) ?>" target="_blank" rel="noopener">
|
<a href="https://erg.be" target="_blank" rel="noopener">
|
||||||
Site de l'erg ↗
|
Site de l'erg ↗
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,17 +110,19 @@ $bodyClass = 'apropos-body';
|
|||||||
<section class="apropos-section" id="apropos-contacts">
|
<section class="apropos-section" id="apropos-contacts">
|
||||||
<h2 class="apropos-section-title">Contacts</h2>
|
<h2 class="apropos-section-title">Contacts</h2>
|
||||||
<div class="apropos-contacts-grid">
|
<div class="apropos-contacts-grid">
|
||||||
<?php foreach ($contacts as $contact): ?>
|
<?php foreach ($contacts as $group): ?>
|
||||||
<address class="apropos-contact-card">
|
<address class="apropos-contact-card">
|
||||||
<strong>
|
<?= renderEntries($group['entries'] ?? []) ?>
|
||||||
<?php if (!empty($contact['url'])): ?>
|
<?php if (!empty($group['role'])): ?>
|
||||||
<a href="<?= htmlspecialchars($contact['url']) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($contact['name']) ?></a>
|
<span><?= htmlspecialchars($group['role']) ?></span>
|
||||||
<?php else: ?>
|
<?php endif; ?>
|
||||||
<?= htmlspecialchars($contact['name']) ?>
|
<?php
|
||||||
<?php endif; ?>
|
// Show the email from the first entry (or any that has one on separate line)
|
||||||
</strong>
|
$emails = array_filter(array_column($group['entries'] ?? [], 'email'), fn($e) => !empty($e));
|
||||||
<span><?= htmlspecialchars($contact['role']) ?></span>
|
foreach ($emails as $email):
|
||||||
<a href="mailto:<?= htmlspecialchars($contact['email']) ?>"><?= htmlspecialchars($contact['email']) ?></a>
|
?>
|
||||||
|
<a href="mailto:<?= htmlspecialchars($email) ?>"><?= htmlspecialchars($email) ?></a>
|
||||||
|
<?php endforeach; ?>
|
||||||
</address>
|
</address>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,16 +134,10 @@ $bodyClass = 'apropos-body';
|
|||||||
<section class="apropos-section" id="apropos-credits">
|
<section class="apropos-section" id="apropos-credits">
|
||||||
<h2 class="apropos-section-title">Crédits</h2>
|
<h2 class="apropos-section-title">Crédits</h2>
|
||||||
<dl class="apropos-credits-list">
|
<dl class="apropos-credits-list">
|
||||||
<?php foreach ($credits as $credit): ?>
|
<?php foreach ($credits as $group): ?>
|
||||||
<div class="apropos-credit-row">
|
<div class="apropos-credit-row">
|
||||||
<dt><?= htmlspecialchars($credit['label']) ?></dt>
|
<dt><?= htmlspecialchars($group['label']) ?></dt>
|
||||||
<dd>
|
<dd><?= renderEntries($group['entries'] ?? []) ?></dd>
|
||||||
<?php if (!empty($credit['url'])): ?>
|
|
||||||
<a href="<?= htmlspecialchars($credit['url']) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($credit['value']) ?></a>
|
|
||||||
<?php else: ?>
|
|
||||||
<?= htmlspecialchars($credit['value']) ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</dd>
|
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</dl>
|
</dl>
|
||||||
|
|||||||
@@ -99,6 +99,10 @@ class AdminAuth
|
|||||||
public static function isAuthenticated(): bool
|
public static function isAuthenticated(): bool
|
||||||
{
|
{
|
||||||
self::startSession();
|
self::startSession();
|
||||||
|
// No password configured → development mode, skip PHP auth.
|
||||||
|
if (!defined('ADMIN_PASSWORD_HASH')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return !empty($_SESSION[self::SESSION_KEY]);
|
return !empty($_SESSION[self::SESSION_KEY]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
46
storage/migrations/011_apropos_entries.sql
Normal file
46
storage/migrations/011_apropos_entries.sql
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
-- Transform apropos data: each row has a label/role and an entries[] of {text, url}.
|
||||||
|
-- Contacts also include email per entry.
|
||||||
|
|
||||||
|
UPDATE apropos_contents SET value = '
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"label": "Design & développement",
|
||||||
|
"entries": [
|
||||||
|
{"text": "Olivia Marly", "url": ""},
|
||||||
|
{"text": "Théophile Gerveau-Mercie", "url": ""},
|
||||||
|
{"text": "Théo Hennequin", "url": ""}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Typographies",
|
||||||
|
"entries": [
|
||||||
|
{"text": "Ductus (Amélie Dumont)", "url": ""},
|
||||||
|
{"text": "BBB DM Sans", "url": ""}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]' WHERE key = 'credits';
|
||||||
|
|
||||||
|
UPDATE apropos_contents SET value = '
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"role": "Bibliothèque d''architecture, d''ingénierie architecturale, d''urbanisme (BAIU) :",
|
||||||
|
"entries": [
|
||||||
|
{"text": "Laurent Leprince", "url": "", "email": "laurent.leprince@uclouvain.be"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Responsable des mémoires de l''ERG :",
|
||||||
|
"entries": [
|
||||||
|
{"text": "Xavier Gorgol", "url": "", "email": "xavier.gorgol@erg.be"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Cours de suivi de mémoire :",
|
||||||
|
"entries": [
|
||||||
|
{"text": "Brigitte Ledune", "url": "", "email": "brigitte.ledune@erg.be"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]' WHERE key = 'contacts';
|
||||||
|
|
||||||
|
-- Remove erg_url from the table (hardcoded in template now)
|
||||||
|
DELETE FROM apropos_contents WHERE key = 'erg_url';
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
-- Optional URL fields for credits and contacts.
|
|
||||||
-- No structural change; items already stored as JSON with optional url keys.
|
|
||||||
-- Default data updated to include urls where applicable.
|
|
||||||
Binary file not shown.
@@ -333,9 +333,50 @@ CREATE TABLE IF NOT EXISTS apropos_contents (
|
|||||||
);
|
);
|
||||||
|
|
||||||
INSERT OR IGNORE INTO apropos_contents (key, value) VALUES
|
INSERT OR IGNORE INTO apropos_contents (key, value) VALUES
|
||||||
('contacts', '[{"name":"Laurent Leprince","role":"Bibliothèque d''architecture, d''ingénierie architecturale, d''urbanisme (BAIU) :","email":"laurent.leprince@uclouvain.be"},{"name":"Xavier Gorgol","role":"Responsable des mémoires de l''ERG :","email":"xavier.gorgol@erg.be"},{"name":"Brigitte Ledune","role":"Cours de suivi de mémoire :","email":"brigitte.ledune@erg.be"}]'),
|
('contacts', '[
|
||||||
('credits', '[{"label":"Design & développement","value":"Olivia Marly, Théophile Gerveau-Mercie & Théo Hennequin"},{"label":"Typographies","value":"Ductus (Amélie Dumont) & BBB DM Sans"}]'),
|
{"role":"Bibliothèque d'architecture, d'ingénierie architecturale, d'urbanisme (BAIU) :", "entries":[
|
||||||
('erg_url', 'https://erg.be');
|
{"text":"Laurent Leprince", "email":"laurent.leprince@uclouvain.be"}
|
||||||
|
]},
|
||||||
|
{"role":"Responsable des mémoires de l'ERG :", "entries":[
|
||||||
|
{"text":"Xavier Gorgol", "email":"xavier.gorgol@erg.be"}
|
||||||
|
]},
|
||||||
|
{"role":"Cours de suivi de mémoire :", "entries":[
|
||||||
|
{"text":"Brigitte Ledune", "email":"brigitte.ledune@erg.be"}
|
||||||
|
]}
|
||||||
|
]'),
|
||||||
|
('credits', '[
|
||||||
|
{"label":"Design & développement", "entries":[
|
||||||
|
{"text":"Olivia Marly"},
|
||||||
|
{"text":"Théophile Gerveau-Mercie"},
|
||||||
|
{"text":"Théo Hennequin"}
|
||||||
|
]},
|
||||||
|
{"label":"Typographies", "entries":[
|
||||||
|
{"text":"Ductus (Amélie Dumont)"},
|
||||||
|
{"text":"BBB DM Sans"}
|
||||||
|
]}
|
||||||
|
]');INSERT OR IGNORE INTO apropos_contents (key, value) VALUES
|
||||||
|
('contacts', '[
|
||||||
|
{"role":"Bibliothèque d\u0027architecture, d\u0027ingénierie architecturale, d\u0027urbanisme (BAIU) :", "entries":[
|
||||||
|
{"text":"Laurent Leprince", "email":"laurent.leprince@uclouvain.be"}
|
||||||
|
]},
|
||||||
|
{"role":"Responsable des mémoires de l\u0027ERG :", "entries":[
|
||||||
|
{"text":"Xavier Gorgol", "email":"xavier.gorgol@erg.be"}
|
||||||
|
]},
|
||||||
|
{"role":"Cours de suivi de mémoire :", "entries":[
|
||||||
|
{"text":"Brigitte Ledune", "email":"brigitte.ledune@erg.be"}
|
||||||
|
]}
|
||||||
|
]'),
|
||||||
|
('credits', '[
|
||||||
|
{"label":"Design & développement", "entries":[
|
||||||
|
{"text":"Olivia Marly"},
|
||||||
|
{"text":"Théophile Gerveau-Mercie"},
|
||||||
|
{"text":"Théo Hennequin"}
|
||||||
|
]},
|
||||||
|
{"label":"Typographies", "entries":[
|
||||||
|
{"text":"Ductus (Amélie Dumont)"},
|
||||||
|
{"text":"BBB DM Sans"}
|
||||||
|
]}
|
||||||
|
]');
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
-- INDEXES for performance
|
-- INDEXES for performance
|
||||||
|
|||||||
Reference in New Issue
Block a user