BROOKO icon
BROOKO UK NETWORK
Where code meets creativity & adventure
File viewer

bootstrap.debug.php

Type
php
Size
8.25 KB
Modified
15 May
bootstrap.debug.php 8.25 KB
<?php
/**
 * WorkersPanel Bootstrap v4.2.14-alpha
 * Matches schema: shared runtime
 */

define('BASE_PATH', __DIR__);
require_once __DIR__ . '/config/version.php';
require_once __DIR__ . '/config/app_paths.php';
if (!defined('DEVELOPMENT')) define('DEVELOPMENT', false);

if (session_status() === PHP_SESSION_NONE) session_start();

// Install check
if (!file_exists(BASE_PATH . '/install.lock')) {
    $uri = $_SERVER['REQUEST_URI'] ?? '';
    if (strpos($uri, '/install') === false) {
        app_redirect('install');
    }
} else {
    $db_config = BASE_PATH . '/config/database.php';
    if (!file_exists($db_config)) {
        http_response_code(500);
        die('Database config missing. Delete install.lock to reinstall.');
    }

    try {
        if (!extension_loaded('pdo_mysql')) {
            throw new RuntimeException('PHP extension pdo_mysql is not enabled on this server.');
        }

        require_once $db_config;

        if (!isset($pdo) || !($pdo instanceof PDO)) {
            throw new RuntimeException('Database bootstrap completed but $pdo was not created.');
        }

        // Set session variables for DB audit triggers
        try {
            $uid = isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : null;
            $ip  = $_SERVER['REMOTE_ADDR'] ?? null;
            $pdo->exec("SET @app_user_id = " . ($uid ?? "NULL"));
            $pdo->exec("SET @app_ip = " . ($ip ? $pdo->quote($ip) : "NULL"));
        } catch (Throwable $e) {}
    } catch (Throwable $e) {
        http_response_code(500);
        $safeMessage = htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
        echo '<!doctype html><html><head><meta charset="utf-8"><title>WorkersPanel DB Error</title>';
        echo '<meta name="viewport" content="width=device-width, initial-scale=1">';
        echo '<style>body{font-family:Arial,sans-serif;background:#111827;color:#f3f4f6;padding:24px;line-height:1.5}';
        echo '.card{max-width:900px;margin:40px auto;background:#1f2937;border:1px solid #374151;border-radius:12px;padding:24px}';
        echo 'code{background:#111827;padding:2px 6px;border-radius:6px} .muted{color:#9ca3af}</style></head><body>';
        echo '<div class="card"><h1>WorkersPanel could not connect to the database</h1>';
        echo '<p><strong>Error:</strong> ' . $safeMessage . '</p>';
        echo '<p class="muted">This is why the site is returning HTTP 500 at startup.</p>';
        echo '<p>Check <code>config/database.php</code>, confirm the database server allows remote connections from this web server, and confirm PHP has <code>pdo_mysql</code> enabled.</p>';
        echo '</div></body></html>';
        exit;
    }
}


// ── DB helpers ─────────────────────────────────────────────

/**
 * Returns true if a column exists on a table in the current database.
 * (Used to keep updates compatible while schema changes roll out.)
 */
function wp_db_column_exists(PDO $pdo, string $table, string $column): bool {
    try {
        $stmt = $pdo->prepare(
            "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ? LIMIT 1"
        );
        $stmt->execute([$table, $column]);
        return (bool)$stmt->fetchColumn();
    } catch (Throwable $e) {
        return false;
    }
}

/**
 * Returns the first existing column from a preferred list, or null.
 */
function wp_db_pick_column(PDO $pdo, string $table, array $candidates): ?string {
    foreach ($candidates as $col) {
        if (wp_db_column_exists($pdo, $table, $col)) return $col;
    }
    return null;
}


// ── Auth helpers ────────────────────────────────────────────

function isLoggedIn(): bool {
    return isset($_SESSION['user_id'], $_SESSION['user_role']);
}

function isAdminRole(): bool {
    return isLoggedIn() && in_array($_SESSION['user_role'], ['administrator', 'director']);
}

function hasPermission(string $permission): bool {
    if (!isLoggedIn()) return false;

    $role = strtolower(trim((string)($_SESSION['user_role'] ?? '')));
    if (in_array($role, ['driver', 'porter'], true) && str_starts_with($permission, 'calendar.')) {
        return false;
    }

    if (isAdminRole()) return true;
    return in_array($permission, $_SESSION['permissions'] ?? [], true);
}

function requireAuth(): void {
    if (!isLoggedIn()) { app_redirect('login'); }
}

function requireAdmin(): void {
    requireAuth();
    if (!isAdminRole()) { http_response_code(403); die('Access denied. Admin required.'); }
}

function requirePermission(string $permission): void {
    requireAuth();
    if (!hasPermission($permission)) { http_response_code(403); die('Access denied.'); }
}

function getCurrentUser(): ?array {
    if (!isLoggedIn()) return null;
    global $pdo;
    try {
        $stmt = $pdo->prepare("SELECT id, username, email, display_name, role, secondary_group FROM users WHERE id = ?");
        $stmt->execute([$_SESSION['user_id']]);
        return $stmt->fetch() ?: null;
    } catch (Throwable $e) { return null; }
}

// ── Activity logging ─────────────────────────────────────────
// Supports both old signature: logActivity($action, $description)
// And new signature: logActivity($action, $entity_type, $entity_id, $description)

function logActivity(string $action, $secondArg = null, $thirdArg = null, $fourthArg = null): void {
    if (!isLoggedIn()) return;
    global $pdo;

    // Detect call signature
    if ($fourthArg !== null) {
        // New: logActivity($action, $entity_type, $entity_id, $description)
        $entity_type = $secondArg;
        $entity_id   = $thirdArg;
        $description = $fourthArg;
    } elseif ($thirdArg !== null && is_int($thirdArg)) {
        // New: logActivity($action, $entity_type, $entity_id)
        $entity_type = $secondArg;
        $entity_id   = $thirdArg;
        $description = null;
    } else {
        // Old: logActivity($action, $description)
        $entity_type = null;
        $entity_id   = null;
        $description = is_string($secondArg) ? $secondArg : null;
    }

    try {
        $pdo->prepare("
            INSERT INTO activity_logs (user_id, action, entity_type, entity_id, description, ip_address, user_agent, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
        ")->execute([
            $_SESSION['user_id'],
            $action,
            $entity_type,
            $entity_id,
            $description,
            $_SERVER['REMOTE_ADDR'] ?? null,
            $_SERVER['HTTP_USER_AGENT'] ?? null,
        ]);
    } catch (Throwable $e) {}
}

// ── DB change log (audit trail) ──────────────────────────────
function logDbChange(string $table, string $action, int $rowId, ?array $oldData = null, ?array $newData = null): void {
    if (!isLoggedIn()) return;
    global $pdo;
    try {
        $pdo->prepare("
            INSERT INTO db_change_logs (user_id, table_name, action, row_id, ip_address, old_data, new_data)
            VALUES (?, ?, ?, ?, ?, ?, ?)
        ")->execute([
            $_SESSION['user_id'],
            $table, $action, $rowId,
            $_SERVER['REMOTE_ADDR'] ?? null,
            $oldData ? json_encode($oldData) : null,
            $newData ? json_encode($newData) : null,
        ]);
    } catch (Throwable $e) {}
}

// ── Utility ─────────────────────────────────────────────────
function e(?string $s): string {
    return htmlspecialchars($s ?? '', ENT_QUOTES, 'UTF-8');
}

function getSystemInfo(string $key, string $default = ''): string {
    global $pdo;
    try {
        $stmt = $pdo->prepare("SELECT value FROM system_info WHERE `key` = ?");
        $stmt->execute([$key]);
        $r = $stmt->fetchColumn();
        return $r !== false ? (string)$r : $default;
    } catch (Throwable $e) { return $default; }
}


app_boot_output_buffer();

date_default_timezone_set('UTC');

if (defined('DEVELOPMENT') && DEVELOPMENT === true) {
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
} else {
    error_reporting(0);
    ini_set('display_errors', 0);
}