HASH_COST]); } /** * Verify password */ function verifyPassword($password, $hash) { return password_verify($password, $hash); } /** * Sanitize input */ function sanitize($input) { if (is_array($input)) { return array_map('sanitize', $input); } return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8'); } /** * Format currency */ function formatCurrency($amount) { return TJJ_CURRENCY_SYMBOL . number_format((float)$amount, 2); } /** * Format date */ function formatDate($date, $format = 'M j, Y') { return date($format, strtotime($date)); } /** * Format datetime */ function formatDateTime($date, $format = 'M j, Y g:i A') { return date($format, strtotime($date)); } /** * JSON response helper */ function jsonResponse($data, $statusCode = 200) { http_response_code($statusCode); header('Content-Type: application/json'); echo json_encode($data); exit; } /** * Redirect helper */ function redirect($url) { header("Location: $url"); exit; } /** * Get current URL */ function currentUrl() { $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http'; return $protocol . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; } /** * Check if request is AJAX */ function isAjax() { return !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'; } /** * Get client IP address */ function getClientIp() { $ip = $_SERVER['REMOTE_ADDR']; if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]; } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } return trim($ip); } /** * Generate CSRF token */ function generateCsrfToken() { if (empty($_SESSION[CSRF_TOKEN_NAME])) { $_SESSION[CSRF_TOKEN_NAME] = bin2hex(random_bytes(32)); } return $_SESSION[CSRF_TOKEN_NAME]; } /** * Verify CSRF token */ function verifyCsrfToken($token) { return isset($_SESSION[CSRF_TOKEN_NAME]) && hash_equals($_SESSION[CSRF_TOKEN_NAME], $token); } /** * Get setting value */ function getSetting($key, $default = null) { $result = db()->fetch( "SELECT setting_value FROM settings WHERE setting_key = :key", ['key' => $key] ); if ($result) { return json_decode($result['setting_value'], true) ?? $result['setting_value']; } return $default; } /** * Update setting value */ function setSetting($key, $value) { $jsonValue = json_encode($value); $existing = db()->fetch( "SELECT id FROM settings WHERE setting_key = :key", ['key' => $key] ); if ($existing) { db()->update('settings', ['setting_value' => $jsonValue], 'setting_key = :key', ['key' => $key]); } else { db()->insert('settings', ['setting_key' => $key, 'setting_value' => $jsonValue]); } } /** * Flash message helpers */ function setFlash($type, $message) { $_SESSION['flash'][$type] = $message; } function getFlash($type) { if (isset($_SESSION['flash'][$type])) { $message = $_SESSION['flash'][$type]; unset($_SESSION['flash'][$type]); return $message; } return null; } function hasFlash($type) { return isset($_SESSION['flash'][$type]); } /** * Pagination helper */ function paginate($totalItems, $currentPage, $perPage = ITEMS_PER_PAGE) { $totalPages = ceil($totalItems / $perPage); $currentPage = max(1, min($currentPage, $totalPages)); $offset = ($currentPage - 1) * $perPage; return [ 'total_items' => $totalItems, 'total_pages' => $totalPages, 'current_page' => $currentPage, 'per_page' => $perPage, 'offset' => $offset, 'has_prev' => $currentPage > 1, 'has_next' => $currentPage < $totalPages ]; } /** * Render pagination HTML */ function renderPagination($pagination, $baseUrl) { if ($pagination['total_pages'] <= 1) return ''; $html = ''; return $html; } /** * Truncate text */ function truncate($text, $length = 100, $suffix = '...') { if (strlen($text) <= $length) return $text; return substr($text, 0, $length) . $suffix; } /** * Slugify text */ function slugify($text) { $text = preg_replace('~[^\pL\d]+~u', '-', $text); $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text); $text = preg_replace('~[^-\w]+~', '', $text); $text = trim($text, '-'); $text = preg_replace('~-+~', '-', $text); return strtolower($text); } /** * Get cart from session */ function getCart() { return $_SESSION['cart'] ?? []; } /** * Add item to cart */ function addToCart($productId, $quantity = 1) { if (!isset($_SESSION['cart'])) { $_SESSION['cart'] = []; } if (isset($_SESSION['cart'][$productId])) { $_SESSION['cart'][$productId] += $quantity; } else { $_SESSION['cart'][$productId] = $quantity; } } /** * Update cart item quantity */ function updateCartItem($productId, $quantity) { if ($quantity <= 0) { removeFromCart($productId); } else { $_SESSION['cart'][$productId] = $quantity; } } /** * Remove item from cart */ function removeFromCart($productId) { unset($_SESSION['cart'][$productId]); } /** * Clear cart */ function clearCart() { $_SESSION['cart'] = []; } /** * Get cart count */ function getCartCount() { return array_sum($_SESSION['cart'] ?? []); } /** * Get cart total */ function getCartTotal() { $total = 0; $cart = getCart(); foreach ($cart as $productId => $quantity) { $product = db()->fetch( "SELECT price, sale_price FROM products WHERE product_id = :id AND is_active = 1", ['id' => $productId] ); if ($product) { $price = $product['sale_price'] ?? $product['price']; $total += $price * $quantity; } } return $total; } /** * Send email via CyberMail API */ function sendEmail($to, $subject, $htmlContent, $textContent = '') { $apiKey = getSetting('cybermail_api_key', defined('CYBERMAIL_API_KEY') ? CYBERMAIL_API_KEY : ''); $from = getSetting('cybermail_from_email', 'noreply@tomsjavajive.com'); $fromName = getSetting('cybermail_from_name', "Tom's Java Jive"); if (!$apiKey) { error_log('[TJJ sendEmail] CYBERMAIL_API_KEY not configured'); return false; } $payload = ['from' => $from, 'to' => $to, 'subject' => $subject, 'html' => $htmlContent]; if ($textContent) $payload['text'] = $textContent; $ch = curl_init('https://platform.cyberpersons.com/email/v1/send'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey, 'Content-Type: application/json'], CURLOPT_TIMEOUT => 20, CURLOPT_SSL_VERIFYPEER => false, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 202) return true; error_log('[TJJ sendEmail] CyberMail HTTP ' . $httpCode . ' — ' . $response); return false; } /** * Log activity */ function logActivity($action, $details = [], $userId = null) { // Implement activity logging if needed }