Initial commit
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
// public/userarea/api/_bootstrap.php
|
||||
// Auth via Laravel Sanctum personal access tokens (PDO only).
|
||||
// Table: auth_personal_access_tokens
|
||||
// Exposes: $pdo (PDO), $iduserlogin (int)
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// CORS for Flutter Web (dev server runs on a different port)
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Headers: Authorization, Content-Type, Accept');
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
header('Access-Control-Max-Age: 86400');
|
||||
|
||||
// Preflight
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(204);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
require_once __DIR__ . '/../class/db-functions.php';
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
/**
|
||||
* Get Authorization header (Apache/Nginx/CGI compatible)
|
||||
*/
|
||||
function getAuthorizationHeader(): ?string
|
||||
{
|
||||
if (function_exists('getallheaders')) {
|
||||
$headers = getallheaders();
|
||||
foreach ($headers as $k => $v) {
|
||||
if (strtolower($k) === 'authorization') return $v;
|
||||
}
|
||||
}
|
||||
if (!empty($_SERVER['HTTP_AUTHORIZATION'])) return $_SERVER['HTTP_AUTHORIZATION'];
|
||||
if (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) return $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract Bearer token
|
||||
*/
|
||||
function getBearerToken(): ?string
|
||||
{
|
||||
$auth = getAuthorizationHeader();
|
||||
if (!$auth) return null;
|
||||
|
||||
if (preg_match('/Bearer\s+(.*)$/i', $auth, $m)) {
|
||||
return trim($m[1]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
$bearer = getBearerToken();
|
||||
if (!$bearer) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Unauthorized (missing Bearer token).']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Sanctum plain token format: "<id>|<plainTextToken>"
|
||||
if (strpos($bearer, '|') === false) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Unauthorized (invalid token format).']);
|
||||
exit;
|
||||
}
|
||||
|
||||
[$tokenId, $plainToken] = explode('|', $bearer, 2);
|
||||
$tokenId = (int)$tokenId;
|
||||
|
||||
if ($tokenId <= 0 || $plainToken === '') {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Unauthorized (invalid token).']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- IMPORTANT: your table name ---
|
||||
$tokensTable = 'auth_personal_access_tokens';
|
||||
|
||||
// Lookup token by id
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, tokenable_id, token, expires_at
|
||||
FROM {$tokensTable}
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$tokenId]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$row) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Unauthorized (token not found).']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Optional expiry check
|
||||
if (!empty($row['expires_at'])) {
|
||||
$now = new DateTimeImmutable('now', new DateTimeZone('Europe/Rome'));
|
||||
$exp = new DateTimeImmutable($row['expires_at'], new DateTimeZone('Europe/Rome'));
|
||||
if ($exp <= $now) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Unauthorized (token expired).']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Sanctum stores SHA-256 hash of the plain token
|
||||
$hash = hash('sha256', $plainToken);
|
||||
if (!hash_equals((string)$row['token'], $hash)) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Unauthorized (token mismatch).']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$iduserlogin = (int)$row['tokenable_id'];
|
||||
if ($iduserlogin <= 0) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Unauthorized (invalid user).']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Update last_used_at (best effort)
|
||||
try {
|
||||
$pdo->prepare("UPDATE {$tokensTable} SET last_used_at = NOW() WHERE id = ?")->execute([$tokenId]);
|
||||
} catch (Throwable $e) {
|
||||
// ignore
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/_bootstrap.php'; // $pdo, $iduserlogin
|
||||
|
||||
try {
|
||||
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
|
||||
|
||||
$cert_id = 0;
|
||||
if ($method === 'DELETE') {
|
||||
$cert_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
} else {
|
||||
// POST fallback
|
||||
$cert_id = isset($_POST['cert_id']) ? (int)$_POST['cert_id'] : (isset($_GET['id']) ? (int)$_GET['id'] : 0);
|
||||
}
|
||||
|
||||
if ($cert_id <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'message' => 'Missing certificate id (id or cert_id).']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get cert and ensure ownership
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, stored_path
|
||||
FROM user_medical_certificates
|
||||
WHERE id = ? AND user_id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$cert_id, $iduserlogin]);
|
||||
$cert = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$cert) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'Certificate not found.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// stored_path like: userarea/certificate/xxx
|
||||
$stored = (string)$cert['stored_path'];
|
||||
$publicRoot = realpath(__DIR__ . '/../../'); // points to /public
|
||||
$fullPath = $publicRoot . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, ltrim($stored, '/\\'));
|
||||
|
||||
if (is_file($fullPath)) {
|
||||
@unlink($fullPath);
|
||||
}
|
||||
|
||||
$del = $pdo->prepare("DELETE FROM user_medical_certificates WHERE id = ? AND user_id = ?");
|
||||
$del->execute([$cert_id, $iduserlogin]);
|
||||
|
||||
echo json_encode(['success' => true, 'deleted_id' => $cert_id], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Server error.', 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/_bootstrap.php'; // $pdo, $iduserlogin
|
||||
|
||||
try {
|
||||
// Optional: school_id filter if you later add it to the table
|
||||
// $school_id = isset($_GET['school_id']) ? (int)$_GET['school_id'] : 0;
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, filename, stored_path, document_name, expiry_date, uploaded_at, notes
|
||||
FROM user_medical_certificates
|
||||
WHERE user_id = ?
|
||||
ORDER BY uploaded_at DESC
|
||||
");
|
||||
$stmt->execute([$iduserlogin]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$now = new DateTimeImmutable('now', new DateTimeZone('Europe/Rome'));
|
||||
|
||||
$certs = array_map(function (array $c) use ($now): array {
|
||||
$expiry = !empty($c['expiry_date']) ? new DateTimeImmutable($c['expiry_date']) : null;
|
||||
$isExpired = $expiry ? ($expiry < $now->setTime(0, 0)) : false;
|
||||
|
||||
return [
|
||||
'id' => (int)$c['id'],
|
||||
'document_name' => (string)($c['document_name'] ?? ''),
|
||||
'filename' => (string)($c['filename'] ?? ''),
|
||||
'stored_path' => (string)($c['stored_path'] ?? ''),
|
||||
'file_url' => '/' . ltrim((string)($c['stored_path'] ?? ''), '/'),
|
||||
'uploaded_at' => $c['uploaded_at'],
|
||||
'expiry_date' => $c['expiry_date'],
|
||||
'is_expired' => $isExpired,
|
||||
'notes' => $c['notes'] ?? null,
|
||||
];
|
||||
}, $rows);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'count' => count($certs),
|
||||
'certificates' => $certs
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Server error.', 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/_bootstrap.php'; // $pdo, $iduserlogin
|
||||
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Method not allowed. Use POST.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($_FILES['certificate']) || ($_FILES['certificate']['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'message' => 'Missing file field: certificate']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$file = $_FILES['certificate'];
|
||||
|
||||
$document_name = trim((string)($_POST['document_name'] ?? 'certificato'));
|
||||
if ($document_name === '') $document_name = 'certificato';
|
||||
|
||||
$expiry_date = (string)($_POST['expiry_date'] ?? '');
|
||||
if ($expiry_date === '' || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $expiry_date)) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'message' => 'expiry_date is required (YYYY-MM-DD)']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$notes = trim((string)($_POST['notes'] ?? ''));
|
||||
|
||||
// Allowed extensions
|
||||
$allowed_ext = ['jpg', 'jpeg', 'png', 'pdf', 'heic', 'heif'];
|
||||
$ext = strtolower(pathinfo((string)$file['name'], PATHINFO_EXTENSION));
|
||||
|
||||
if (!in_array($ext, $allowed_ext, true)) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'message' => 'Unsupported format. Allowed: jpg, jpeg, png, pdf, heic, heif']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ((int)$file['size'] > 10 * 1024 * 1024) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'message' => 'File too large (max 10MB)']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Upload dir: ../certificate relative to /public/userarea/api
|
||||
$upload_dir = realpath(__DIR__ . '/..') . DIRECTORY_SEPARATOR . 'certificate' . DIRECTORY_SEPARATOR;
|
||||
if (!is_dir($upload_dir)) {
|
||||
mkdir($upload_dir, 0755, true);
|
||||
}
|
||||
|
||||
$safe_name = preg_replace('/[^a-zA-Z0-9\._-]/', '_', basename((string)$file['name']));
|
||||
$new_filename = $iduserlogin . '-' . time() . '-' . $safe_name;
|
||||
|
||||
$destination = $upload_dir . $new_filename;
|
||||
|
||||
if (!move_uploaded_file((string)$file['tmp_name'], $destination)) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Error saving the file.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stored_path = 'userarea/certificate/' . $new_filename;
|
||||
|
||||
// OPTIONAL: if you later add school_id column, include it here too.
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO user_medical_certificates
|
||||
(user_id, filename, stored_path, document_name, expiry_date, notes, uploaded_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, NOW())
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$iduserlogin,
|
||||
(string)$file['name'],
|
||||
$stored_path,
|
||||
$document_name,
|
||||
$expiry_date,
|
||||
$notes
|
||||
]);
|
||||
|
||||
$newId = (int)$pdo->lastInsertId();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'certificate' => [
|
||||
'id' => $newId,
|
||||
'document_name' => $document_name,
|
||||
'filename' => (string)$file['name'],
|
||||
'stored_path' => $stored_path,
|
||||
'file_url' => '/' . $stored_path,
|
||||
'expiry_date' => $expiry_date,
|
||||
'notes' => $notes !== '' ? $notes : null
|
||||
]
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Server error.', 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
// public/userarea/api/my_lessons.php
|
||||
// GET ?school_id=3&month=2025-12
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/_bootstrap.php'; // gives $pdo, $iduserlogin, $authUser
|
||||
|
||||
try {
|
||||
$school_id = isset($_GET['school_id']) ? (int)$_GET['school_id'] : 0;
|
||||
if ($school_id <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'message' => 'Missing required parameter: school_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$month = $_GET['month'] ?? (new DateTimeImmutable('now', new DateTimeZone('Europe/Rome')))->format('Y-m');
|
||||
if (!preg_match('/^\d{4}-\d{2}$/', (string)$month)) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid month format. Use YYYY-MM.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$tz = new DateTimeZone('Europe/Rome');
|
||||
$monthDate = DateTimeImmutable::createFromFormat('Y-m', $month, $tz) ?: new DateTimeImmutable('now', $tz);
|
||||
|
||||
$startOfMonth = $monthDate->format('Y-m-01'); // inclusive
|
||||
$endOfMonth = $monthDate->modify('first day of next month')->format('Y-m-d'); // exclusive
|
||||
$prevMonth = $monthDate->modify('-1 month')->format('Y-m');
|
||||
$nextMonth = $monthDate->modify('+1 month')->format('Y-m');
|
||||
|
||||
// --- Authorization: user must belong to that school ---
|
||||
// Adjust if your authorization logic differs.
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT 1
|
||||
FROM user_schools
|
||||
WHERE user_id = ?
|
||||
AND school_id = ?
|
||||
AND status = 'active'
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$iduserlogin, $school_id]);
|
||||
if (!$stmt->fetchColumn()) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'message' => 'Forbidden: user not allowed for this school.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- School info (same as your page) ---
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, name, address_street, address_city, address_province
|
||||
FROM schools
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$school_id]);
|
||||
$school = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$school) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'School not found.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Main query (ported from your PHP page) ---
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
sb.id as booking_id,
|
||||
sb.status,
|
||||
sb.order_id,
|
||||
cs.id as session_id,
|
||||
cs.session_date,
|
||||
cs.start_time,
|
||||
cs.end_time,
|
||||
cs.room_name,
|
||||
c.name as class_name,
|
||||
ct.level,
|
||||
o.available_entries,
|
||||
o.available_recoveries
|
||||
FROM session_bookings sb
|
||||
JOIN class_sessions cs ON sb.session_id = cs.id
|
||||
JOIN class_types ct ON cs.class_type_id = ct.id
|
||||
JOIN classes c ON ct.class_id = c.id
|
||||
JOIN orders o ON sb.order_id = o.id
|
||||
WHERE sb.user_id = ?
|
||||
AND cs.school_id = ?
|
||||
AND cs.session_date >= ?
|
||||
AND cs.session_date < ?
|
||||
ORDER BY cs.session_date ASC, cs.start_time ASC
|
||||
");
|
||||
$stmt->execute([$iduserlogin, $school_id, $startOfMonth, $endOfMonth]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Better 24h rule: compare against actual lesson start datetime
|
||||
$now = new DateTimeImmutable('now', $tz);
|
||||
$limit = $now->modify('+24 hours');
|
||||
|
||||
$lessons = [];
|
||||
foreach ($rows as $r) {
|
||||
$startDt = DateTimeImmutable::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$r['session_date'] . ' ' . $r['start_time'],
|
||||
$tz
|
||||
);
|
||||
|
||||
if (!$startDt) {
|
||||
// fallback if start_time is H:i
|
||||
$startDt = DateTimeImmutable::createFromFormat(
|
||||
'Y-m-d H:i',
|
||||
$r['session_date'] . ' ' . substr((string)$r['start_time'], 0, 5),
|
||||
$tz
|
||||
);
|
||||
}
|
||||
|
||||
$canModify = false;
|
||||
if ($r['status'] === 'booked' && $startDt instanceof DateTimeImmutable) {
|
||||
$canModify = ($startDt > $limit);
|
||||
}
|
||||
|
||||
$lessons[] = [
|
||||
'booking_id' => (int)$r['booking_id'],
|
||||
'status' => (string)$r['status'],
|
||||
'order_id' => (int)$r['order_id'],
|
||||
|
||||
'session' => [
|
||||
'session_id' => (int)$r['session_id'],
|
||||
'date' => (string)$r['session_date'],
|
||||
'start_time' => (string)$r['start_time'],
|
||||
'end_time' => (string)$r['end_time'],
|
||||
'room_name' => $r['room_name'] !== null ? (string)$r['room_name'] : null,
|
||||
'start_datetime_iso' => $startDt ? $startDt->format(DateTimeInterface::ATOM) : null,
|
||||
],
|
||||
|
||||
'class' => [
|
||||
'name' => (string)$r['class_name'],
|
||||
'level' => ($r['level'] ?? null) ? (string)$r['level'] : null,
|
||||
],
|
||||
|
||||
'wallet' => [
|
||||
'available_entries' => (int)($r['available_entries'] ?? 0),
|
||||
'available_recoveries' => (int)($r['available_recoveries'] ?? 0),
|
||||
],
|
||||
|
||||
'can_reschedule' => $canModify,
|
||||
'can_cancel' => $canModify,
|
||||
'can_modify' => $canModify,
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'month' => $monthDate->format('Y-m'),
|
||||
'prev_month' => $prevMonth,
|
||||
'next_month' => $nextMonth,
|
||||
'school' => [
|
||||
'id' => (int)$school['id'],
|
||||
'name' => (string)$school['name'],
|
||||
'address_street' => $school['address_street'] ?? null,
|
||||
'address_city' => $school['address_city'] ?? null,
|
||||
'address_province' => $school['address_province'] ?? null,
|
||||
'address_full' => trim(
|
||||
($school['address_street'] ?? '') . ', ' .
|
||||
($school['address_city'] ?? '') . ' ' .
|
||||
($school['address_province'] ?? '')
|
||||
),
|
||||
],
|
||||
'lessons' => $lessons
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Server error.',
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/_bootstrap.php'; // $pdo, $iduserlogin
|
||||
|
||||
try {
|
||||
$school_id = isset($_GET['school_id']) ? (int)$_GET['school_id'] : 0;
|
||||
|
||||
if ($school_id <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'message' => 'Missing school_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Security: user must be active in this school ---
|
||||
$chk = $pdo->prepare("
|
||||
SELECT 1
|
||||
FROM user_schools us
|
||||
JOIN schools s ON s.id = us.school_id
|
||||
WHERE us.user_id = ?
|
||||
AND us.school_id = ?
|
||||
AND us.status = 'active'
|
||||
AND s.status = 'active'
|
||||
LIMIT 1
|
||||
");
|
||||
$chk->execute([$iduserlogin, $school_id]);
|
||||
|
||||
if (!$chk->fetchColumn()) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'message' => 'Forbidden: user not allowed for this school']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Defaults (same as your include) ---
|
||||
$defaults = [
|
||||
'portal_purchases_enabled' => 1,
|
||||
'allowed_product_types' => 'subscription,carnet,drop_in',
|
||||
'payment_methods' => 'manual',
|
||||
'currency_code' => 'EUR',
|
||||
'enable_notifications' => 1,
|
||||
'allow_freeze_global' => 1,
|
||||
'freeze_max_days_global' => 30,
|
||||
'auto_propagate_on_purchase' => 1,
|
||||
'allow_full_access_rebooking' => 1,
|
||||
// Add here any other defaults you want to guarantee
|
||||
];
|
||||
|
||||
// --- Load settings row ---
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM school_settings
|
||||
WHERE school_id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$school_id]);
|
||||
$settings = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$settings) {
|
||||
// Create row with defaults (only school_id is required by your schema)
|
||||
$ins = $pdo->prepare("INSERT INTO school_settings (school_id) VALUES (?)");
|
||||
$ins->execute([$school_id]);
|
||||
|
||||
// Reload
|
||||
$stmt = $pdo->prepare("SELECT * FROM school_settings WHERE school_id = ? LIMIT 1");
|
||||
$stmt->execute([$school_id]);
|
||||
$settings = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
|
||||
// Merge defaults (fallback for NULL / missing fields)
|
||||
$schoolSettings = array_merge($defaults, $settings);
|
||||
|
||||
// Ensure arrays
|
||||
$paymentMethods = array_values(array_filter(array_map('trim', explode(',', (string)($schoolSettings['payment_methods'] ?? '')))));
|
||||
$productTypes = array_values(array_filter(array_map('trim', explode(',', (string)($schoolSettings['allowed_product_types'] ?? '')))));
|
||||
|
||||
$schoolSettings['payment_methods_array'] = $paymentMethods;
|
||||
$schoolSettings['allowed_product_types_array'] = $productTypes;
|
||||
|
||||
// Optional: cast some known int flags to int (helps Flutter)
|
||||
foreach (
|
||||
[
|
||||
'portal_purchases_enabled',
|
||||
'enable_notifications',
|
||||
'allow_freeze_global',
|
||||
'freeze_max_days_global',
|
||||
'auto_propagate_on_purchase',
|
||||
'allow_full_access_rebooking'
|
||||
] as $k
|
||||
) {
|
||||
if (isset($schoolSettings[$k])) {
|
||||
$schoolSettings[$k] = is_numeric($schoolSettings[$k]) ? (int)$schoolSettings[$k] : $schoolSettings[$k];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'school_id' => $school_id,
|
||||
'settings' => $schoolSettings
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Server error.',
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
// public/userarea/api/api_user_schools.php
|
||||
// Returns ONLY the schools where the authenticated user is enrolled.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/_bootstrap.php'; // provides $pdo, $iduserlogin
|
||||
|
||||
try {
|
||||
// Optional user info for greeting
|
||||
$stmt = $pdo->prepare("SELECT first_name, avatar FROM auth_users WHERE id = ? LIMIT 1");
|
||||
$stmt->execute([$iduserlogin]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
|
||||
// User schools only
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
s.id,
|
||||
s.name,
|
||||
s.logo,
|
||||
s.address_street,
|
||||
s.address_postal_code,
|
||||
s.address_city,
|
||||
s.address_province,
|
||||
s.address_country
|
||||
FROM user_schools us
|
||||
JOIN schools s ON us.school_id = s.id
|
||||
WHERE us.user_id = ?
|
||||
AND us.status = 'active'
|
||||
AND s.status = 'active'
|
||||
ORDER BY s.name
|
||||
");
|
||||
$stmt->execute([$iduserlogin]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$schools = array_map(function (array $s): array {
|
||||
$street = trim((string)($s['address_street'] ?? ''));
|
||||
$zip = trim((string)($s['address_postal_code'] ?? ''));
|
||||
$city = trim((string)($s['address_city'] ?? ''));
|
||||
$prov = trim((string)($s['address_province'] ?? ''));
|
||||
$cntry = trim((string)($s['address_country'] ?? ''));
|
||||
|
||||
$line2Parts = [];
|
||||
if ($zip !== '') $line2Parts[] = $zip;
|
||||
if ($city !== '') $line2Parts[] = $city;
|
||||
|
||||
$line2 = implode(' ', $line2Parts);
|
||||
if ($prov !== '') $line2 .= ' (' . $prov . ')';
|
||||
if ($cntry !== '') $line2 .= ' - ' . $cntry;
|
||||
|
||||
return [
|
||||
'id' => (int)$s['id'],
|
||||
'name' => (string)($s['name'] ?? ''),
|
||||
'logo' => ($s['logo'] ?? null) !== null && trim((string)$s['logo']) !== '' ? (string)$s['logo'] : null,
|
||||
'address_street' => $street !== '' ? $street : null,
|
||||
'address_postal_code' => $zip !== '' ? $zip : null,
|
||||
'address_city' => $city !== '' ? $city : null,
|
||||
'address_province' => $prov !== '' ? $prov : null,
|
||||
'address_country' => $cntry !== '' ? $cntry : null,
|
||||
'address_full' => trim($street . ($street && $line2 ? ', ' : '') . $line2),
|
||||
];
|
||||
}, $rows);
|
||||
|
||||
$autoSelect = (count($schools) === 1);
|
||||
$selectedSchoolId = $autoSelect ? (int)$schools[0]['id'] : null;
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'auto_select' => $autoSelect,
|
||||
'selected_school_id' => $selectedSchoolId,
|
||||
'user' => [
|
||||
'id' => $iduserlogin,
|
||||
'first_name' => $user['first_name'] ?? null,
|
||||
'avatar' => $user['avatar'] ?? null,
|
||||
],
|
||||
'schools' => $schools,
|
||||
'message' => empty($schools)
|
||||
? 'No active schools assigned to this user. Please contact your school.'
|
||||
: null
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Server error.',
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/_bootstrap.php'; // $pdo, $iduserlogin
|
||||
|
||||
try {
|
||||
$user_id = (int)$iduserlogin;
|
||||
if ($user_id <= 0) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$defaults = [
|
||||
'notify_email' => 1,
|
||||
'notify_whatsapp' => 0,
|
||||
'notify_push' => 0,
|
||||
'notify_booking_confirm' => 1,
|
||||
'notify_booking_cancel' => 1,
|
||||
'notify_session_cancel' => 1,
|
||||
'notify_payment_receipt' => 1,
|
||||
'notify_expiration_reminder' => 1,
|
||||
'newsletter_opt_in' => 0,
|
||||
'marketing_opt_in' => 0,
|
||||
'locale' => 'it',
|
||||
'timezone' => 'Europe/Rome',
|
||||
];
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM user_settings
|
||||
WHERE user_id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$user_id]);
|
||||
$settings = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$settings) {
|
||||
// Create row with defaults (user_id only required by your schema)
|
||||
$ins = $pdo->prepare("INSERT INTO user_settings (user_id) VALUES (?)");
|
||||
$ins->execute([$user_id]);
|
||||
|
||||
// Reload
|
||||
$stmt = $pdo->prepare("SELECT * FROM user_settings WHERE user_id = ? LIMIT 1");
|
||||
$stmt->execute([$user_id]);
|
||||
$settings = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
|
||||
$userSettings = array_merge($defaults, $settings);
|
||||
|
||||
// Cast numeric flags to int for Flutter
|
||||
foreach (
|
||||
[
|
||||
'notify_email',
|
||||
'notify_whatsapp',
|
||||
'notify_push',
|
||||
'notify_booking_confirm',
|
||||
'notify_booking_cancel',
|
||||
'notify_session_cancel',
|
||||
'notify_payment_receipt',
|
||||
'notify_expiration_reminder',
|
||||
'newsletter_opt_in',
|
||||
'marketing_opt_in',
|
||||
] as $k
|
||||
) {
|
||||
if (isset($userSettings[$k])) {
|
||||
$userSettings[$k] = is_numeric($userSettings[$k]) ? (int)$userSettings[$k] : $userSettings[$k];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'user_id' => $user_id,
|
||||
'settings' => $userSettings
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Server error.', 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/_bootstrap.php'; // $pdo, $iduserlogin
|
||||
|
||||
try {
|
||||
if (strtoupper($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Method not allowed. Use POST.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = (int)$iduserlogin;
|
||||
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = json_decode($raw ?: '', true);
|
||||
if (!is_array($data)) $data = $_POST;
|
||||
|
||||
// Whitelist fields you allow to be updated
|
||||
$allowed = [
|
||||
'notify_email',
|
||||
'notify_whatsapp',
|
||||
'notify_push',
|
||||
'notify_booking_confirm',
|
||||
'notify_booking_cancel',
|
||||
'notify_session_cancel',
|
||||
'notify_payment_receipt',
|
||||
'notify_expiration_reminder',
|
||||
'newsletter_opt_in',
|
||||
'marketing_opt_in',
|
||||
'locale',
|
||||
'timezone',
|
||||
];
|
||||
|
||||
$updates = [];
|
||||
$params = [];
|
||||
|
||||
foreach ($allowed as $field) {
|
||||
if (array_key_exists($field, $data)) {
|
||||
$val = $data[$field];
|
||||
|
||||
// Normalize booleans/numbers for tinyint fields
|
||||
if (is_bool($val)) $val = $val ? 1 : 0;
|
||||
|
||||
$updates[] = "{$field} = ?";
|
||||
$params[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($updates)) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'message' => 'No valid fields to update.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Ensure row exists
|
||||
$pdo->prepare("INSERT IGNORE INTO user_settings (user_id) VALUES (?)")->execute([$user_id]);
|
||||
|
||||
$params[] = $user_id;
|
||||
|
||||
$sql = "UPDATE user_settings SET " . implode(', ', $updates) . " WHERE user_id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Server error.', 'error' => $e->getMessage()]);
|
||||
}
|
||||
Reference in New Issue
Block a user