mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
feat: add objet field (tfe/thèse/frart) with share-link restriction and site-settings toggles
This commit is contained in:
1
TODO.md
1
TODO.md
@@ -11,4 +11,5 @@
|
|||||||
- [x] Extract form CSS into `form.css`; load it in admin add/edit via `$extraCss` and in student partage form directly; `system.css` now only used by `system.php`; `partage/thanks.php` rewritten to use design-system classes
|
- [x] Extract form CSS into `form.css`; load it in admin add/edit via `$extraCss` and in student partage form directly; `system.css` now only used by `system.php`; `partage/thanks.php` rewritten to use design-system classes
|
||||||
- [x] Fix student form: add missing `v_smtp_active` view to `schema.sql` (SMTP was silently skipped on fresh installs); fix `thanks.php` redirect (was `/partage/thanks.php` — blocked by nginx PHP deny rule); route `/partage/thanks` through `index.php` special-case handler
|
- [x] Fix student form: add missing `v_smtp_active` view to `schema.sql` (SMTP was silently skipped on fresh installs); fix `thanks.php` redirect (was `/partage/thanks.php` — blocked by nginx PHP deny rule); route `/partage/thanks` through `index.php` special-case handler
|
||||||
- [x] Merge all migration SQL into schema.sql; delete migrations/ folder; simplify migrate.sh (009 share_links, 014 ap_programs, 011 apropos seed, missing semicolon fix)
|
- [x] Merge all migration SQL into schema.sql; delete migrations/ folder; simplify migrate.sh (009 share_links, 014 ap_programs, 011 apropos seed, missing semicolon fix)
|
||||||
|
- [x] Add `objet` field (tfe/thèse/frart) to theses; `objet_restriction` on share_links; objet_these/frart_enabled site_settings; wire into partage form, parametres, and acces-etudiante
|
||||||
- [x] Fix student form scroll (add `overflow-y:auto` to `.student-body`); move all remaining inline styles from partage error/password-gate pages into `form.css`
|
- [x] Fix student form scroll (add `overflow-y:auto` to `.student-body`); move all remaining inline styles from partage error/password-gate pages into `form.css`
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ switch ($action) {
|
|||||||
$expiresRaw = !empty($_POST['expires_at']) ? trim($_POST['expires_at']) : null;
|
$expiresRaw = !empty($_POST['expires_at']) ? trim($_POST['expires_at']) : null;
|
||||||
$expiresAt = null;
|
$expiresAt = null;
|
||||||
if ($expiresRaw) {
|
if ($expiresRaw) {
|
||||||
// datetime-local gives "YYYY-MM-DDTHH:MM"
|
|
||||||
$expiresAt = date('Y-m-d H:i:s', strtotime($expiresRaw));
|
$expiresAt = date('Y-m-d H:i:s', strtotime($expiresRaw));
|
||||||
if ($expiresAt <= date('Y-m-d H:i:s')) {
|
if ($expiresAt <= date('Y-m-d H:i:s')) {
|
||||||
App::redirect('/admin/acces-etudiante.php', error: "La date d'expiration doit être dans le futur.");
|
App::redirect('/admin/acces-etudiante.php', error: "La date d'expiration doit être dans le futur.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$shareLink->create(1, $password, $expiresAt);
|
$objetRaw = $_POST['objet_restriction'] ?? '';
|
||||||
|
$objetRestriction = in_array($objetRaw, ['tfe', 'thèse', 'frart'], true) ? $objetRaw : null;
|
||||||
|
$shareLink->create(1, $password, $expiresAt, $objetRestriction);
|
||||||
App::redirect('/admin/acces-etudiante.php', success: 'Lien d\'accès créé.');
|
App::redirect('/admin/acces-etudiante.php', success: 'Lien d\'accès créé.');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,16 @@ $db = new Database();
|
|||||||
$section = $_POST['section'] ?? '';
|
$section = $_POST['section'] ?? '';
|
||||||
|
|
||||||
if ($section === 'formulaire') {
|
if ($section === 'formulaire') {
|
||||||
// Save access-type toggle settings
|
|
||||||
$allowed = ['access_type_libre_enabled', 'access_type_interne_enabled', 'access_type_interdit_enabled'];
|
$allowed = ['access_type_libre_enabled', 'access_type_interne_enabled', 'access_type_interdit_enabled'];
|
||||||
foreach ($allowed as $key) {
|
foreach ($allowed as $key) {
|
||||||
$value = isset($_POST[$key]) ? '1' : '0';
|
$value = isset($_POST[$key]) ? '1' : '0';
|
||||||
$db->setSetting($key, $value);
|
$db->setSetting($key, $value);
|
||||||
}
|
}
|
||||||
App::flash('success', "Paramètres du formulaire mis à jour.");
|
App::flash('success', "Paramètres du formulaire mis à jour.");
|
||||||
|
} elseif ($section === 'objet_types') {
|
||||||
|
$db->setSetting('objet_these_enabled', isset($_POST['objet_these_enabled']) ? '1' : '0');
|
||||||
|
$db->setSetting('objet_frart_enabled', isset($_POST['objet_frart_enabled']) ? '1' : '0');
|
||||||
|
App::flash('success', "Types de travaux mis à jour.");
|
||||||
} elseif ($section === 'smtp') {
|
} elseif ($section === 'smtp') {
|
||||||
$smtpData = [
|
$smtpData = [
|
||||||
'host' => $_POST['smtp_host'] ?? '',
|
'host' => $_POST['smtp_host'] ?? '',
|
||||||
|
|||||||
@@ -185,6 +185,19 @@ function renderShareLinkForm(string $slug, array $link): void
|
|||||||
$formData = $_SESSION['form_data_share_' . $slug] ?? [];
|
$formData = $_SESSION['form_data_share_' . $slug] ?? [];
|
||||||
unset($_SESSION['form_data_share_' . $slug]);
|
unset($_SESSION['form_data_share_' . $slug]);
|
||||||
|
|
||||||
|
// Determine allowed objet values for this link
|
||||||
|
$siteSettings = Database::getInstance()->getAllSettings();
|
||||||
|
$objetRestriction = $link['objet_restriction'] ?? null;
|
||||||
|
if ($objetRestriction !== null) {
|
||||||
|
// Link is locked to one type — always show only that
|
||||||
|
$allowedObjet = [$objetRestriction];
|
||||||
|
} else {
|
||||||
|
// Build from enabled site settings
|
||||||
|
$allowedObjet = ['tfe'];
|
||||||
|
if (($siteSettings['objet_these_enabled'] ?? '1') === '1') $allowedObjet[] = 'thèse';
|
||||||
|
if (($siteSettings['objet_frart_enabled'] ?? '1') === '1') $allowedObjet[] = 'frart';
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a CSRF token specific to this share link (stored in session)
|
// Generate a CSRF token specific to this share link (stored in session)
|
||||||
$shareCsrfKey = 'share_csrf_' . $slug;
|
$shareCsrfKey = 'share_csrf_' . $slug;
|
||||||
if (empty($_SESSION[$shareCsrfKey])) {
|
if (empty($_SESSION[$shareCsrfKey])) {
|
||||||
@@ -236,6 +249,23 @@ function renderShareLinkForm(string $slug, array $link): void
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Informations du TFE</legend>
|
<legend>Informations du TFE</legend>
|
||||||
|
|
||||||
|
<?php if (count($allowedObjet) > 1): ?>
|
||||||
|
<div class="admin-form-group">
|
||||||
|
<label>Type de travail : <span class="asterisk">*</span></label>
|
||||||
|
<div class="form-radio-group">
|
||||||
|
<?php foreach ($allowedObjet as $objetVal): ?>
|
||||||
|
<label class="admin-checkbox-label">
|
||||||
|
<input type="radio" name="objet" value="<?= htmlspecialchars($objetVal) ?>"
|
||||||
|
<?= (old($formData, 'objet') ?: $allowedObjet[0]) === $objetVal ? 'checked' : '' ?> required>
|
||||||
|
<?= htmlspecialchars(ucfirst($objetVal)) ?>
|
||||||
|
</label>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<input type="hidden" name="objet" value="<?= htmlspecialchars($allowedObjet[0]) ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php $name = 'titre'; $label = 'Titre :'; $value = old($formData, 'titre'); $required = true; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
<?php $name = 'titre'; $label = 'Titre :'; $value = old($formData, 'titre'); $required = true; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||||
<?php $name = 'subtitle'; $label = 'Sous-titre (si applicable) :'; $value = old($formData, 'subtitle'); $required = false; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
<?php $name = 'subtitle'; $label = 'Sous-titre (si applicable) :'; $value = old($formData, 'subtitle'); $required = false; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||||
<?php $name = 'auteurice'; $label = 'Auteur·ice(s) :'; $value = old($formData, 'auteurice'); $required = true; $attrs = ['autocomplete' => 'name']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
<?php $name = 'auteurice'; $label = 'Auteur·ice(s) :'; $value = old($formData, 'auteurice'); $required = true; $attrs = ['autocomplete' => 'name']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ class ThesisCreateController
|
|||||||
'baiu_link' => $data['lien'],
|
'baiu_link' => $data['lien'],
|
||||||
'license_id' => $data['licenseId'],
|
'license_id' => $data['licenseId'],
|
||||||
'access_type_id' => $data['accessTypeId'],
|
'access_type_id' => $data['accessTypeId'],
|
||||||
|
'objet' => $data['objet'],
|
||||||
'author_id' => $authorId,
|
'author_id' => $authorId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -275,6 +276,10 @@ class ThesisCreateController
|
|||||||
$accessTypeId = 2; // Interne
|
$accessTypeId = 2; // Interne
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Objet — restricted to valid values
|
||||||
|
$validObjet = ['tfe', 'thèse', 'frart'];
|
||||||
|
$objet = in_array($post['objet'] ?? '', $validObjet, true) ? $post['objet'] : 'tfe';
|
||||||
|
|
||||||
// External link (optional)
|
// External link (optional)
|
||||||
$lien = '';
|
$lien = '';
|
||||||
if (!empty($post['lien'])) {
|
if (!empty($post['lien'])) {
|
||||||
@@ -298,7 +303,7 @@ class ThesisCreateController
|
|||||||
'auteurName', 'mail', 'showContact', 'confirmationEmail', 'annee', 'orientationId', 'apProgramId',
|
'auteurName', 'mail', 'showContact', 'confirmationEmail', 'annee', 'orientationId', 'apProgramId',
|
||||||
'finalityId', 'titre', 'subtitle', 'synopsis', 'durationInfo',
|
'finalityId', 'titre', 'subtitle', 'synopsis', 'durationInfo',
|
||||||
'juryMembers', 'keywords', 'languageIds', 'formatIds',
|
'juryMembers', 'keywords', 'languageIds', 'formatIds',
|
||||||
'licenseId', 'lien', 'accessTypeId'
|
'licenseId', 'lien', 'accessTypeId', 'objet'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1566,11 +1566,15 @@ class Database {
|
|||||||
synopsis, file_size_info,
|
synopsis, file_size_info,
|
||||||
baiu_link, license_id,
|
baiu_link, license_id,
|
||||||
access_type_id,
|
access_type_id,
|
||||||
|
objet,
|
||||||
is_published,
|
is_published,
|
||||||
submitted_at
|
submitted_at
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP)
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP)
|
||||||
");
|
");
|
||||||
|
|
||||||
|
$validObjet = ['tfe', 'thèse', 'frart'];
|
||||||
|
$objet = in_array($data['objet'] ?? '', $validObjet, true) ? $data['objet'] : 'tfe';
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$identifier,
|
$identifier,
|
||||||
$data['title'],
|
$data['title'],
|
||||||
@@ -1584,6 +1588,7 @@ class Database {
|
|||||||
!empty($data['baiu_link']) ? $data['baiu_link'] : null,
|
!empty($data['baiu_link']) ? $data['baiu_link'] : null,
|
||||||
isset($data['license_id']) ? $data['license_id'] : null,
|
isset($data['license_id']) ? $data['license_id'] : null,
|
||||||
isset($data['access_type_id']) ? (int)$data['access_type_id'] : 2, // default: Interne
|
isset($data['access_type_id']) ? (int)$data['access_type_id'] : 2, // default: Interne
|
||||||
|
$objet,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$thesisId = (int)$this->pdo->lastInsertId();
|
$thesisId = (int)$this->pdo->lastInsertId();
|
||||||
|
|||||||
@@ -48,16 +48,20 @@ class ShareLink
|
|||||||
* @param string|null $expiresAt ISO-8601 expiration date, null = never expires
|
* @param string|null $expiresAt ISO-8601 expiration date, null = never expires
|
||||||
* @return array The created link row
|
* @return array The created link row
|
||||||
*/
|
*/
|
||||||
public function create(int $createdBy, ?string $password = null, ?string $expiresAt = null): array
|
public function create(int $createdBy, ?string $password = null, ?string $expiresAt = null, ?string $objetRestriction = null): array
|
||||||
{
|
{
|
||||||
$slug = self::generateSlug();
|
$slug = self::generateSlug();
|
||||||
$passwordHash = $password !== null ? password_hash($password, PASSWORD_BCRYPT) : null;
|
$passwordHash = $password !== null ? password_hash($password, PASSWORD_BCRYPT) : null;
|
||||||
|
$validObjet = ['tfe', 'thèse', 'frart'];
|
||||||
|
$objetRestriction = ($objetRestriction !== null && in_array($objetRestriction, $validObjet, true))
|
||||||
|
? $objetRestriction
|
||||||
|
: null;
|
||||||
|
|
||||||
$stmt = $this->db->getConnection()->prepare(
|
$stmt = $this->db->getConnection()->prepare(
|
||||||
"INSERT INTO share_links (slug, password_hash, is_active, created_by, expires_at)
|
"INSERT INTO share_links (slug, objet_restriction, password_hash, is_active, created_by, expires_at)
|
||||||
VALUES (?, ?, 1, ?, ?)"
|
VALUES (?, ?, ?, 1, ?, ?)"
|
||||||
);
|
);
|
||||||
$stmt->execute([$slug, $passwordHash, $createdBy, $expiresAt]);
|
$stmt->execute([$slug, $objetRestriction, $passwordHash, $createdBy, $expiresAt]);
|
||||||
|
|
||||||
return $this->findBySlug($slug);
|
return $this->findBySlug($slug);
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -163,6 +163,7 @@ CREATE TABLE IF NOT EXISTS theses (
|
|||||||
|
|
||||||
-- Type of work
|
-- Type of work
|
||||||
is_doctoral BOOLEAN DEFAULT 0, -- 0 for TFE, 1 for doctoral thesis
|
is_doctoral BOOLEAN DEFAULT 0, -- 0 for TFE, 1 for doctoral thesis
|
||||||
|
objet TEXT NOT NULL DEFAULT 'tfe' CHECK (objet IN ('tfe', 'thèse', 'frart')),
|
||||||
|
|
||||||
-- Academic details
|
-- Academic details
|
||||||
orientation_id INTEGER,
|
orientation_id INTEGER,
|
||||||
@@ -295,7 +296,9 @@ INSERT OR IGNORE INTO site_settings (key, value) VALUES
|
|||||||
('access_type_interdit_enabled', '1'),
|
('access_type_interdit_enabled', '1'),
|
||||||
('access_type_interne_enabled', '1'),
|
('access_type_interne_enabled', '1'),
|
||||||
('access_type_libre_enabled', '0'),
|
('access_type_libre_enabled', '0'),
|
||||||
('admin_password_hash', '');
|
('admin_password_hash', ''),
|
||||||
|
('objet_these_enabled', '1'),
|
||||||
|
('objet_frart_enabled', '1');
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
-- STATIC PAGES / CONTENT MANAGEMENT
|
-- STATIC PAGES / CONTENT MANAGEMENT
|
||||||
@@ -331,6 +334,7 @@ INSERT OR IGNORE INTO pages (slug, title, content) VALUES
|
|||||||
CREATE TABLE IF NOT EXISTS share_links (
|
CREATE TABLE IF NOT EXISTS share_links (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
slug TEXT NOT NULL UNIQUE, -- Format: YYYYMMDD-<random>, e.g. 20260416-a3f9k2
|
slug TEXT NOT NULL UNIQUE, -- Format: YYYYMMDD-<random>, e.g. 20260416-a3f9k2
|
||||||
|
objet_restriction TEXT CHECK (objet_restriction IN ('tfe', 'thèse', 'frart')), -- NULL = no restriction
|
||||||
password_hash TEXT, -- bcrypt hash; NULL = no password required
|
password_hash TEXT, -- bcrypt hash; NULL = no password required
|
||||||
is_active INTEGER NOT NULL DEFAULT 1, -- 1 = active, 0 = disabled
|
is_active INTEGER NOT NULL DEFAULT 1, -- 1 = active, 0 = disabled
|
||||||
usage_count INTEGER NOT NULL DEFAULT 0, -- Number of successful submissions via this link
|
usage_count INTEGER NOT NULL DEFAULT 0, -- Number of successful submissions via this link
|
||||||
@@ -478,6 +482,7 @@ SELECT
|
|||||||
t.subtitle,
|
t.subtitle,
|
||||||
t.year,
|
t.year,
|
||||||
t.is_doctoral,
|
t.is_doctoral,
|
||||||
|
t.objet,
|
||||||
o.name as orientation,
|
o.name as orientation,
|
||||||
ap.name as ap_program,
|
ap.name as ap_program,
|
||||||
ft.name as finality_type,
|
ft.name as finality_type,
|
||||||
|
|||||||
Binary file not shown.
@@ -16,6 +16,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Lien</th>
|
<th scope="col">Lien</th>
|
||||||
|
<th scope="col">Objet</th>
|
||||||
<th scope="col">Statut</th>
|
<th scope="col">Statut</th>
|
||||||
<th scope="col">Mot de passe</th>
|
<th scope="col">Mot de passe</th>
|
||||||
<th scope="col">Utilisations</th>
|
<th scope="col">Utilisations</th>
|
||||||
@@ -48,6 +49,13 @@
|
|||||||
<code style="font-size:var(--step--2);color:var(--text-secondary);"><?= htmlspecialchars($link['slug']) ?></code>
|
<code style="font-size:var(--step--2);color:var(--text-secondary);"><?= htmlspecialchars($link['slug']) ?></code>
|
||||||
<input type="hidden" id="url-<?= $link['id'] ?>" value="<?= $fullUrl ?>">
|
<input type="hidden" id="url-<?= $link['id'] ?>" value="<?= $fullUrl ?>">
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php if ($link['objet_restriction']): ?>
|
||||||
|
<span class="status-badge"><?= htmlspecialchars($link['objet_restriction']) ?></span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span style="color:var(--text-secondary);font-size:var(--step--2);">Tous</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?php if ($isExpired): ?>
|
<?php if ($isExpired): ?>
|
||||||
<span class="status-badge status-pending"><?= $statusLabel ?></span>
|
<span class="status-badge status-pending"><?= $statusLabel ?></span>
|
||||||
@@ -114,6 +122,16 @@
|
|||||||
<form method="post" action="actions/acces-etudiante.php" class="admin-form">
|
<form method="post" action="actions/acces-etudiante.php" class="admin-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="action" value="create">
|
<input type="hidden" name="action" value="create">
|
||||||
|
<div>
|
||||||
|
<label for="create-objet">Type d'objet (optionnel)</label>
|
||||||
|
<select id="create-objet" name="objet_restriction">
|
||||||
|
<option value="">— Tous les types —</option>
|
||||||
|
<option value="tfe">TFE</option>
|
||||||
|
<option value="thèse">Thèse</option>
|
||||||
|
<option value="frart">Frart</option>
|
||||||
|
</select>
|
||||||
|
<small>Restreint ce lien à un seul type de soumission.</small>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="create-password">Mot de passe (optionnel)</label>
|
<label for="create-password">Mot de passe (optionnel)</label>
|
||||||
<input type="password" id="create-password" name="password" autocomplete="new-password">
|
<input type="password" id="create-password" name="password" autocomplete="new-password">
|
||||||
|
|||||||
@@ -106,6 +106,52 @@
|
|||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════════════
|
||||||
|
TYPES D'OBJET
|
||||||
|
══════════════════════════════════════════════════════════════ -->
|
||||||
|
<section aria-labelledby="settings-objet-title">
|
||||||
|
<h2 id="settings-objet-title">Types de travaux</h2>
|
||||||
|
<p>Active ou désactive les types de travaux dans les formulaires et la consultation. Un type désactivé ne peut plus être soumis ni affiché sur le site.</p>
|
||||||
|
<p class="param-note">Le type <strong>TFE</strong> est toujours actif et ne peut pas être désactivé.</p>
|
||||||
|
|
||||||
|
<form method="post" action="actions/settings.php" class="param-form">
|
||||||
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
|
<input type="hidden" name="section" value="objet_types">
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Types disponibles</legend>
|
||||||
|
|
||||||
|
<label class="param-checkbox param-checkbox--disabled">
|
||||||
|
<input type="checkbox" disabled checked>
|
||||||
|
<span>
|
||||||
|
<strong>TFE</strong><br>
|
||||||
|
<small>Travail de fin d'études — toujours actif</small>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="param-checkbox">
|
||||||
|
<input type="checkbox" name="objet_these_enabled" value="1"
|
||||||
|
<?= ($siteSettings['objet_these_enabled'] ?? '1') === '1' ? 'checked' : '' ?>>
|
||||||
|
<span>
|
||||||
|
<strong>Thèse</strong><br>
|
||||||
|
<small>Thèses doctorales</small>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="param-checkbox">
|
||||||
|
<input type="checkbox" name="objet_frart_enabled" value="1"
|
||||||
|
<?= ($siteSettings['objet_frart_enabled'] ?? '1') === '1' ? 'checked' : '' ?>>
|
||||||
|
<span>
|
||||||
|
<strong>Frart</strong><br>
|
||||||
|
<small>Formation de recherche en art</small>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<button type="submit">Enregistrer</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- ══════════════════════════════════════════════════════════════
|
<!-- ══════════════════════════════════════════════════════════════
|
||||||
RELAY SMTP
|
RELAY SMTP
|
||||||
══════════════════════════════════════════════════════════════ -->
|
══════════════════════════════════════════════════════════════ -->
|
||||||
|
|||||||
Reference in New Issue
Block a user