From c850a451692683966ba5770a06cc575355871cb3 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 25 Feb 2026 17:24:28 +0000 Subject: [PATCH] Autosave: 20260225-172428 --- admin/index.php | 3 + includes/functions.php | 230 ++++++++++++++++++++++++++++++++++++----- login.php | 2 +- 3 files changed, 211 insertions(+), 24 deletions(-) diff --git a/admin/index.php b/admin/index.php index 98edba2..8704f70 100644 --- a/admin/index.php +++ b/admin/index.php @@ -2,6 +2,9 @@ require_once __DIR__ . '/../db/config.php'; require_once __DIR__ . '/../includes/functions.php'; +// Ensure user is logged in first +require_login(); + $pdo = db(); require_permission('dashboard_view'); diff --git a/includes/functions.php b/includes/functions.php index 04a1f32..b803e60 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -60,22 +60,196 @@ function get_product_price($product) { return $price; } -function get_base_url() { - $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443 ? "https://" : "http://"; - $domainName = $_SERVER['HTTP_HOST']; - - // Check for Flatlogic VM base URL (if needed) - // For now, standard detection - $path = str_replace(basename($_SERVER['SCRIPT_NAME']), "", $_SERVER['SCRIPT_NAME']); - - // Normalize path: ensure it ends with / - if (substr($path, -1) !== '/') { - $path .= '/'; +/** + * Paginate a query result. + * + * @param PDO $pdo The PDO connection object. + * @param string $query The base SQL query (without LIMIT/OFFSET). + * @param array $params Query parameters. + * @param int $default_limit Default items per page. + * @return array Pagination result with keys: data, total_rows, total_pages, current_page, limit. + */ +function paginate_query($pdo, $query, $params = [], $default_limit = 20) { + // Get current page + $page = isset($_GET['page']) && is_numeric($_GET['page']) ? (int)$_GET['page'] : 1; + if ($page < 1) $page = 1; + + // Get limit (allow 20, 50, 100, or -1 for all) + $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : $default_limit; + // Validate limit + if ($limit != -1 && !in_array($limit, [20, 50, 100])) { + $limit = $default_limit; } - // If we are in admin/ or api/ or other subdirs, we might need to go up - // But this function is usually called from root files or with knowledge of its location - return $protocol . $domainName . $path; + // If limit is -1, fetch all + if ($limit == -1) { + $stmt = $pdo->prepare($query); + $stmt->execute($params); + $data = $stmt->fetchAll(); + return [ + 'data' => $data, + 'total_rows' => count($data), + 'total_pages' => 1, + 'current_page' => 1, + 'limit' => -1 + ]; + } + + // Count total rows using a subquery to handle complex queries safely + $count_sql = "SELECT COUNT(*) FROM ($query) as count_table"; + $stmt = $pdo->prepare($count_sql); + $stmt->execute($params); + $total_rows = $stmt->fetchColumn(); + + $total_pages = ceil($total_rows / $limit); + if ($page > $total_pages && $total_pages > 0) $page = $total_pages; + + // Calculate offset + $offset = ($page - 1) * $limit; + if ($offset < 0) $offset = 0; + + // Add LIMIT and OFFSET + $query_with_limit = $query . " LIMIT " . (int)$limit . " OFFSET " . (int)$offset; + + $stmt = $pdo->prepare($query_with_limit); + $stmt->execute($params); + $data = $stmt->fetchAll(); + + return [ + 'data' => $data, + 'total_rows' => $total_rows, + 'total_pages' => $total_pages, + 'current_page' => $page, + 'limit' => $limit + ]; +} + +/** + * Render pagination controls and limit selector. + * + * @param array $pagination The result array from paginate_query. + * @param array $extra_params Additional GET parameters to preserve. + */ +function render_pagination_controls($pagination, $extra_params = []) { + $page = $pagination['current_page']; + $total_pages = $pagination['total_pages']; + $limit = $pagination['limit']; + + // Build query string for limit change + $params = array_merge($_GET, $extra_params); + unset($params['page']); // Reset page when limit changes + + // Limit Selector + $limits = [20, 50, 100, -1]; + echo '
'; + + echo '
'; + echo '
'; + // Preserve other GET params + foreach ($params as $key => $val) { + if ($key !== 'limit') echo ''; + } + echo 'SHOW:'; + echo ''; + echo '
'; + + // Total Count + echo 'Total: ' . $pagination['total_rows'] . ' items'; + + // Optional Total Amount (Sum) + if (isset($pagination['total_amount_sum'])) { + echo 'Total Sum: ' . format_currency($pagination['total_amount_sum']) . ''; + } + + if ($total_pages > 0) { + echo 'Page ' . $page . ' of ' . $total_pages . ''; + } + echo '
'; + + // Pagination Links + if ($total_pages > 1) { + echo ''; + } + echo '
'; +} + +/** + * Get the project root URL + */ +function get_base_url() { + $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || ($_SERVER['SERVER_PORT'] ?? 80) == 443 ? "https://" : "http://"; + $host = $_SERVER['HTTP_HOST'] ?? 'localhost'; + + $script = $_SERVER['SCRIPT_NAME'] ?? '/index.php'; + $path = dirname($script); + $path = str_replace('\\', '/', $path); // Corrected escaping for backslash + + $subdirs = ['/admin', '/api', '/includes', '/db', '/mail', '/ai', '/assets']; + foreach ($subdirs as $dir) { + if ($path === $dir || str_ends_with($path, $dir)) { + $path = substr($path, 0, -strlen($dir)); + break; + } + } + + if ($path === '' || $path === '.') $path = '/'; + return $protocol . $host . rtrim($path, '/') . '/'; +} + +if (!function_exists('str_ends_with')) { + function str_ends_with($haystack, $needle) { + $length = strlen($needle); + if (!$length) return true; + return substr($haystack, -$length) === $needle; + } } /** @@ -90,7 +264,7 @@ function init_session() { ini_set('session.gc_maxlifetime', (string)$lifetime); // Set cookie parameters before session_start - $isSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443; + $isSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || ($_SERVER['SERVER_PORT'] ?? 80) == 443; session_set_cookie_params([ 'lifetime' => $lifetime, @@ -103,11 +277,6 @@ function init_session() { session_start(); } - - // Refresh session expiration on each load if user is logged in - if (isset($_SESSION['user'])) { - // Optional: you could implement a last_activity check here - } } function login_user($username, $password) { @@ -151,14 +320,29 @@ function has_permission($permission) { $user = get_logged_user(); if (!$user) return false; - // Admin has all permissions - if ($user['permissions'] === 'all') return true; + // If permissions are missing from session (stale session), fetch from DB + if (!isset($user['permissions'])) { + $pdo = db(); + $stmt = $pdo->prepare("SELECT g.permissions FROM users u JOIN user_groups g ON u.group_id = g.id WHERE u.id = ?"); + $stmt->execute([$user['id']]); + $perms = $stmt->fetchColumn(); + $_SESSION['user']['permissions'] = $perms; + $user['permissions'] = $perms; + } + + $userPerms = $user['permissions'] ?: ''; + + // Admin has all permissions + if ($userPerms === 'all') return true; + + $permissions = explode(',', $userPerms); + $permissions = array_map('trim', $permissions); - $permissions = json_decode($user['permissions'] ?: '[]', true); return in_array('all', $permissions) || in_array($permission, $permissions); } function require_permission($permission) { + require_login(); if (!has_permission($permission)) { die("Access Denied: You do not have permission to view this page ($permission)."); } diff --git a/login.php b/login.php index 6cb50d4..4222c74 100644 --- a/login.php +++ b/login.php @@ -12,7 +12,7 @@ $baseUrl = get_base_url(); function get_redirect_url($baseUrl) { if (has_permission('dashboard_view')) { return $baseUrl . 'admin/index.php'; - } elseif (has_permission('pos')) { + } elseif (has_permission('pos_view')) { return $baseUrl . 'pos.php'; } elseif (has_permission('kitchen_view')) { return $baseUrl . 'kitchen.php';