Publier Globinours 1.0.0-rc.3

This commit is contained in:
Alexandre NOEL 2026-09-03 12:39:15 +02:00
commit 9a2b4068da
325 changed files with 38230 additions and 20 deletions

View file

@ -0,0 +1,141 @@
<?php
$configuredRetention = AppSettings::int('backup_retention', 1, 50);
$formatSize = static function (int $bytes): string {
$decimal = I18n::locale() === 'fr' ? ',' : '.';
if ($bytes >= 1_048_576) {
return number_format($bytes / 1_048_576, 1, $decimal, ' ') . ' ' . t('unit.mb');
}
return number_format($bytes / 1024, 0, $decimal, ' ') . ' ' . t('unit.kb');
};
?>
<?php require __DIR__ . '/settings_nav.php'; ?>
<div class="mb-3">
<h2 class="mb-1"><?= h(t('backups.heading')) ?></h2>
<div class="text-muted"><?= h(t('backups.intro')) ?></div>
</div>
<?php if ($status === 'created'): ?><div class="alert alert-success">✅ <?= h(t('backups.created')) ?></div>
<?php endif; ?>
<?php if ($status === 'deleted'): ?><div class="alert alert-success">✅ <?= h(t('backups.deleted')) ?></div>
<?php endif; ?>
<?php if ($status === 'verified'): ?><div class="alert alert-success">✅ <?= h(t('backups.verified')) ?></div>
<?php endif; ?>
<?php if ($status === 'schedule'): ?><div class="alert alert-success">✅ <?= h(t('backups.schedule_saved')) ?></div>
<?php endif; ?>
<?php if ($error !== ''): ?><div class="alert alert-danger"><strong><?= h(t('backups.failed')) ?></strong>
<?= h($error) ?></div><?php endif; ?>
<div class="row g-3 mb-3">
<div class="col-lg-7">
<div class="card h-100">
<div class="card-header">
<h3 class="card-title"><?= h(t('backups.create')) ?></h3>
</div>
<div class="card-body">
<p><?= h(t('backups.create_help')) ?></p>
<div class="text-muted small"><?= h(t('backups.manifest_help', ['count' => $configuredRetention])) ?>
</div>
</div>
<div class="card-footer text-end">
<form method="post" action="/settings/backups/create"><button class="btn btn-primary">💾
<?= h(t('backups.create_now')) ?></button></form>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="card border-danger h-100 overflow-hidden">
<div class="card-header">
<h3 class="card-title text-danger"><?= h(t('backups.restore')) ?></h3>
</div>
<form method="post" action="/settings/backups/restore" enctype="multipart/form-data"
onsubmit="return confirm(<?= h(json_encode(t('backups.restore_confirm'), JSON_HEX_APOS | JSON_HEX_QUOT)) ?>);">
<div class="card-body">
<label class="form-label"><?= h(t('backups.archive')) ?></label>
<input class="form-control" type="file" name="backup" accept=".zip,application/zip" required>
<label class="form-check mt-3"><input class="form-check-input" type="checkbox" required><span
class="form-check-label"><?= h(t('backups.understand')) ?></span></label>
</div>
<div class="card-footer text-end"><button class="btn btn-danger">
<?= h(t('backups.restore_archive')) ?></button></div>
</form>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<h3 class="card-title"> <?= h(t('backups.automatic')) ?></h3>
</div>
<form method="post" action="/settings/backups/schedule"><input type="hidden" name="csrf"
value="<?= h(Auth::csrf()) ?>">
<div class="card-body row g-3 align-items-end">
<div class="col-md"><label class="form-check"><input class="form-check-input" type="checkbox"
name="backup_schedule_enabled"
<?= AppSettings::get('backup_schedule_enabled') === '1' ? 'checked' : '' ?>><span
class="form-check-label"><?= h(t('backups.enable_schedule')) ?></span></label>
<div class="form-hint"><?= h(t('backups.schedule_help')) ?></div>
</div>
<div class="col-md-3"><label class="form-label"><?= h(t('notification.frequency')) ?></label><select
class="form-select" name="backup_schedule_frequency">
<option value="daily"
<?= AppSettings::get('backup_schedule_frequency') === 'daily' ? 'selected' : '' ?>>
<?= h(t('notification.daily')) ?></option>
<option value="weekly"
<?= AppSettings::get('backup_schedule_frequency') === 'weekly' ? 'selected' : '' ?>>
<?= h(t('notification.weekly')) ?></option>
</select></div>
<div class="col-auto"><button class="btn btn-primary"><?= h(t('common.save')) ?></button></div>
<div class="col-12 small text-muted"><code>php <?= h(dirname(__DIR__, 2) . '/scripts/maintenance.php') ?>
run</code> · <?= h(t('backups.last_verified')) ?> :
<?= h(AppSettings::get('backup_last_verified_at') ?: '—') ?></div>
</div>
</form>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title"><?= h(t('backups.available')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th><?= h(t('common.date')) ?></th>
<th><?= h(t('backups.archive_column')) ?></th>
<th><?= h(t('backups.size')) ?></th>
<th class="w-1"></th>
</tr>
</thead>
<tbody>
<?php foreach ($backups as $backup): ?>
<tr>
<td class="text-nowrap"><?= h($backup['date']) ?></td>
<td><strong><?= h($backup['name']) ?></strong><?php if (str_contains($backup['name'], 'avant-restauration')): ?>
<span class="badge bg-orange-lt"><?= h(t('backups.emergency')) ?></span><?php endif; ?></td>
<td><?= h($formatSize((int)$backup['size'])) ?></td>
<td>
<div class="d-flex gap-1">
<form method="post" action="/settings/backups/verify"><input type="hidden" name="csrf"
value="<?= h(Auth::csrf()) ?>"><input type="hidden" name="file"
value="<?= h($backup['name']) ?>"><button class="btn btn-outline-success btn-sm">
<?= h(t('backups.verify')) ?></button></form><a
class="btn btn-outline-primary btn-sm text-nowrap"
href="/settings/backups/download?file=<?= rawurlencode($backup['name']) ?>">
<?= h(t('backups.download')) ?></a>
<form method="post" action="/settings/backups/delete"
onsubmit="return confirm(<?= h(json_encode(t('backups.delete_confirm'), JSON_HEX_APOS | JSON_HEX_QUOT)) ?>);">
<input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>"><input type="hidden"
name="file" value="<?= h($backup['name']) ?>"><button
class="btn btn-outline-danger btn-sm" title="<?= h(t('common.delete')) ?>"
aria-label="<?= h(t('common.delete')) ?>">🗑</button></form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php if (!$backups): ?><tr>
<td colspan="4" class="text-center text-muted py-4"><?= h(t('backups.none')) ?></td>
</tr><?php endif; ?>
</tbody>
</table>
</div>
</div>