Files
andreandClaude Fable 5 3f2c505c17 feat(rgpd): pedido de acesso/apagamento de dados + processo escrito (item 9.5)
- migration_20260925_rgpd_requests.sql: tabela rgpd_requests (email, tipo,
  estado, consentimento).
- config/functions_rgpd.php: rgpd_find() (acesso, art.º 15) e rgpd_erase()
  (apagamento por anonimização, art.º 17) sobre membership_leads/trial_leads/
  event_leads, + CRUD dos pedidos.
- Política de Privacidade: formulário público (POST + CSRF + anti-spam) para
  pedir acesso ou apagamento, e o processo escrito (resposta em 30 dias).
- Backoffice: página "Pedidos RGPD" (nav + index) para exportar os dados de um
  email (CSV, audit) ou anonimizá-los (POST + CSRF, audit).

O email está desligado, por isso os pedidos ficam guardados (como as leads),
não dependem de SMTP. Texto legal com placeholders — rever com jurista antes
do go-live (como já constava na Política).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-25 23:48:18 +01:00

75 lines
2.6 KiB
PHP

<?php
// RGPD — pedidos de acesso (art.º 15.º) e apagamento (art.º 17.º).
//
// Os formulários públicos guardam PII em membership_leads, trial_leads e
// event_leads, sempre identificável pelo email. O "apagamento" aqui é
// anonimização: a linha fica (estatística/audit), mas o nome/email/telefone
// passam a placeholders — o dado pessoal deixa de existir sem quebrar nada.
function rgpd_pii_tables(): array {
return [
'membership_leads' => 'Pedidos de inscrição',
'trial_leads' => 'Aulas experimentais',
'event_leads' => 'Inscrições em eventos',
];
}
// Devolve os dados pessoais associados a um email, organizados por tabela.
function rgpd_find(PDO $pdo, string $email): array {
$out = [];
foreach (rgpd_pii_tables() as $table => $label) {
$st = $pdo->prepare(
"SELECT name, email, phone, message, created_at FROM `$table` WHERE email = ? ORDER BY created_at DESC"
);
$st->execute([$email]);
$rows = $st->fetchAll(PDO::FETCH_ASSOC);
if ($rows) $out[$table] = $rows;
}
return $out;
}
// Anonimiza os dados pessoais de um email em todas as tabelas públicas.
// Devolve o número de registos alterados.
function rgpd_erase(PDO $pdo, string $email): int {
$n = 0;
foreach (array_keys(rgpd_pii_tables()) as $table) {
$st = $pdo->prepare(
"UPDATE `$table` SET name = 'Apagado (RGPD)', email = '', phone = '', message = '' WHERE email = ?"
);
$st->execute([$email]);
$n += $st->rowCount();
}
return $n;
}
function db_rgpd_request_create(PDO $pdo, array $d): int {
$st = $pdo->prepare(
"INSERT INTO rgpd_requests (gym_id, email, request_type, message, consent) VALUES (?, ?, ?, ?, ?)"
);
$st->execute([
(int) ($d['gym_id'] ?? 0),
(string) $d['email'],
(string) $d['request_type'],
(string) ($d['message'] ?? ''),
(int) ($d['consent'] ?? 0),
]);
return (int) $pdo->lastInsertId();
}
function db_rgpd_request_get(PDO $pdo, int $id): ?array {
$st = $pdo->prepare("SELECT * FROM rgpd_requests WHERE id = ? LIMIT 1");
$st->execute([$id]);
return $st->fetch(PDO::FETCH_ASSOC) ?: null;
}
function db_rgpd_requests_list(PDO $pdo): array {
return $pdo->query(
"SELECT * FROM rgpd_requests WHERE deleted_at IS NULL ORDER BY created_at DESC"
)->fetchAll(PDO::FETCH_ASSOC);
}
function db_rgpd_request_mark(PDO $pdo, int $id, string $status, int $by): void {
$st = $pdo->prepare("UPDATE rgpd_requests SET status = ?, processed_at = NOW(), processed_by = ? WHERE id = ?");
$st->execute([$status, $by, $id]);
}