Files
fitme3070.pt/shared/platform-tools/index.php
T
andreandClaude Opus 5 4d40adbf64 Vendor platform-tools: everything this site runs is now in one repo
shared/platform-tools was a git submodule. On this branch it is a plain
directory of tracked files, frozen at ea3616abae30106df283e73a3e2f9e5bb0e6ae90
— the same commit fotografia.andmore.pt pins. Contents are byte-identical to
that tree, verified blob by blob.

The submodule caused every deployment failure this site has had:

  - .gitmodules named no `branch`, so the update fell back to
    refs/remotes/origin/HEAD — a ref a plain server-side clone never writes.
    "Atualizar submódulo" failed with "Unable to find refs/remotes/origin/HEAD
    revision in submodule path".
  - Its URL was http://, which the origin answers with 523.
  - This install is deployed by uploading files, and a submodule is invisible
    to that. The tree moved forward while shared/platform-tools stayed on the
    old pin, config/functions_secrets.php died on a missing compat_secrets.php,
    and the whole site — public pages included — went down.

Each of those was separately fixable. Together they mean a second repository
nobody can see from the deployment path, gating whether the first one boots. A
plain directory deploys like every other file and cannot fall out of step.

The cost is that platform-tools updates no longer arrive by bumping a pin; they
are a copy-over-and-commit. shared/platform-tools/VENDORED.md records the
upstream SHA, the procedure, and which four files differ from upstream in line
endings only (social/*.js, .css, functions_publications.php — CRLF upstream,
normalised to LF here; content otherwise identical).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 23:06:23 +01:00

129 lines
6.1 KiB
PHP

<?php
require_once __DIR__ . '/functions_settings.php'; // platform_admin_url()
// ─── Shared sysadmin hub ─────────────────────────────────────────────────────
//
// Landing page for the sysadmin area. The host navbar points only here; every
// other sysadmin tool is reached via the cards on this page.
//
// Bootstrap contract — stub at <host>/views/sysadmin/index.php must:
// 1. Load $pdo (config/db.php + functions.php)
// 2. Run require_sysadmin()
// 3. Set $repo_root
// Optional overrides (same as platform_update.php / backups.php):
// $platform_layout_header / $platform_layout_navbar / $platform_layout_footer
// $sysadmin_tools — array of card definitions; merged with the defaults below.
// Each entry: ['title'=>str, 'description'=>str, 'icon'=>str (bi-*),
// 'url'=>str, 'badge'=>?str, 'color'=>str (primary/info/…)]
if (!isset($repo_root) || !is_string($repo_root)) {
$repo_root = realpath(dirname(__DIR__, 2)) ?: dirname(__DIR__, 2);
}
$repo_root = rtrim($repo_root, '/');
$base = function (string $path): string {
return function_exists('base_url') ? base_url($path) : '/' . ltrim($path, '/');
};
$default_tools = [
[
'title' => 'Atualizar plataforma',
'description' => 'Git pull/push, OPcache, migrations e log de erros.',
'icon' => 'bi-arrow-repeat',
'url' => platform_admin_url('platform_update.php'),
'color' => 'primary',
],
[
'title' => 'Backups',
'description' => 'Criar e transferir cópias de segurança dos uploads e da base de dados.',
'icon' => 'bi-archive',
'url' => platform_admin_url('backups.php'),
'color' => 'secondary',
],
[
'title' => 'Estado SMTP',
'description' => 'Configuração do envio de email, teste de ligação e mensagem de teste.',
'icon' => 'bi-envelope-check',
'url' => platform_admin_url('smtp_status.php'),
'color' => 'info',
],
// Por omissão e não site a site: a partir do momento em que o cliente do hub
// é partilhado, uma plataforma que o tenha instalado sem mostrar a página
// fica ligada ao hub sem ninguém saber por onde a desligar.
[
'title' => 'Integração com o hub',
'description' => 'Anunciar esta plataforma à Frota e gerir as chaves de leitura e de operações.',
'icon' => 'bi-hdd-network',
'url' => platform_admin_url('hub_integration.php'),
'color' => 'secondary',
// Um site que ainda não tenha o stub não deve mostrar um cartão para 404.
'available' => is_file($repo_root . '/public/views/sysadmin/hub_integration.php')
|| is_file($repo_root . '/public/views/admin/hub_integration.php'),
],
];
// Um site pode esconder um cartão por omissão declarando
// $platform_hide_tools = ['smtp_status.php'] no seu stub. Serve para quando a
// plataforma tem uma página própria que já faz o mesmo -- o dance tem as
// Comunicações, que editam a configuração e correm os mesmos testes, e ter os
// dois cartões lado a lado só faz hesitar em qual clicar.
$hide = array_map('strval', $platform_hide_tools ?? []);
if ($hide) {
$default_tools = array_filter(
$default_tools,
fn($t) => !array_filter($hide, fn($h) => str_contains((string) $t['url'], $h))
);
}
// Cards podem declarar 'available' => bool para serem escondidos quando o
// servidor não suporta a ferramenta (ex.: exige proc_open). Ausente = visível.
$tools = array_filter(
array_merge($default_tools, $sysadmin_tools ?? []),
fn($t) => !isset($t['available']) || $t['available']
);
$layout_header = $platform_layout_header ?? ($repo_root . '/includes/header.php');
$layout_navbar = $platform_layout_navbar ?? ($repo_root . '/includes/navbar.php');
$layout_footer = $platform_layout_footer ?? ($repo_root . '/includes/footer.php');
$pageTitle = 'Sistema';
if (is_file($layout_header)) include $layout_header;
if (is_file($layout_navbar)) include $layout_navbar;
?>
<div class="container py-4" style="max-width:1000px;">
<div class="mb-4">
<h2 class="mb-1"><i class="bi bi-shield-lock me-2"></i>Sistema</h2>
<p class="text-muted small mb-0">Ferramentas de administração da plataforma.</p>
</div>
<div class="row g-3">
<?php foreach ($tools as $t): ?>
<div class="col-12 col-md-6">
<a href="<?= htmlspecialchars($t['url']) ?>"
class="card shadow-sm h-100 text-decoration-none text-reset border-0">
<div class="card-body d-flex gap-3">
<div class="flex-shrink-0">
<span class="d-inline-flex align-items-center justify-content-center rounded-3 text-bg-<?= htmlspecialchars($t['color'] ?? 'primary') ?>"
style="width:48px;height:48px;font-size:1.5rem;">
<i class="bi <?= htmlspecialchars($t['icon'] ?? 'bi-tools') ?>"></i>
</span>
</div>
<div class="flex-grow-1">
<div class="d-flex align-items-center gap-2">
<h5 class="mb-0"><?= htmlspecialchars($t['title']) ?></h5>
<?php if (!empty($t['badge'])): ?>
<span class="badge text-bg-warning"><?= htmlspecialchars($t['badge']) ?></span>
<?php endif; ?>
</div>
<p class="text-muted small mb-0 mt-1"><?= htmlspecialchars($t['description']) ?></p>
</div>
<div class="flex-shrink-0 align-self-center">
<i class="bi bi-chevron-right text-muted"></i>
</div>
</div>
</a>
</div>
<?php endforeach; ?>
</div>
</div>
<?php if (is_file($layout_footer)) include $layout_footer; ?>