Файловый менеджер - Редактировать - /home/gqdcvggs/go.imators.com/aktas.zip
Назад
PK \8�[�b�F F index.htmlnu �Iw�� <!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Test Tailwind CSS</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body class="bg-gray-100 min-h-screen"> <header class="bg-blue-600 text-white shadow-lg"> <div class="container mx-auto px-4 py-6"> <h1 class="text-3xl font-bold">Imators</h1> <p class="text-blue-100">Solutions web innovantes</p> </div> </header> <main class="container mx-auto px-4 py-8"> <section class="bg-white rounded-lg shadow-md p-6 mb-8"> <h2 class="text-2xl font-bold text-gray-800 mb-4">Bienvenue sur notre page de test</h2> <p class="text-gray-600 mb-4">Cette page démontre l'utilisation de Tailwind CSS pour créer des interfaces modernes.</p> <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> En savoir plus </button> </section> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div class="bg-white rounded-lg shadow-md p-6"> <h3 class="font-bold text-xl mb-2 text-blue-600">Service 1</h3> <p class="text-gray-600">Description du premier service que nous proposons.</p> </div> <div class="bg-white rounded-lg shadow-md p-6"> <h3 class="font-bold text-xl mb-2 text-blue-600">Service 2</h3> <p class="text-gray-600">Description du deuxième service que nous proposons.</p> </div> <div class="bg-white rounded-lg shadow-md p-6"> <h3 class="font-bold text-xl mb-2 text-blue-600">Service 3</h3> <p class="text-gray-600">Description du troisième service que nous proposons.</p> </div> </div> </main> <footer class="bg-gray-800 text-white mt-12 py-8"> <div class="container mx-auto px-4"> <div class="flex flex-col md:flex-row justify-between"> <div class="mb-4 md:mb-0"> <h4 class="font-bold text-lg mb-2">Imators</h4> <p class="text-gray-400">© 2025 Imators. Tous droits réservés.</p> </div> <div> <h4 class="font-bold text-lg mb-2">Contact</h4> <p class="text-gray-400">info@imators.com</p> <p class="text-gray-400">+33 1 23 45 67 89</p> </div> </div> </div> </footer> </body> </html>PK \8�[bU�{ login/login.phpnu �Iw�� i love le cacaPK \8�[6O}� .htaccessnu �[��� # php -- BEGIN cPanel-generated handler, do not edit # Set the “ea-php81” package as the default “PHP” programming language. <IfModule mime_module> AddHandler application/x-httpd-ea-php81 .php .php8 .phtml </IfModule> # php -- END cPanel-generated handler, do not edit PK \8�[��t�# �# app-en/join.phpnu �Iw�� <?php require_once 'config.php'; $code = isset($_GET['code']) ? $_GET['code'] : ''; if (empty($code)) { header('Location: /index.php?error=invalid_code'); exit; } // Vérifier si le code d'invitation est valide $stmt = $db->prepare("SELECT i.*, f.family_name FROM family_invites i JOIN families f ON i.family_id = f.id WHERE i.invite_code = ? AND i.accepted = 0 AND i.created_at > DATE_SUB(NOW(), INTERVAL 7 DAY)"); $stmt->execute([$code]); $invite = $stmt->fetch(); if (!$invite) { header('Location: /index.php?error=expired_code'); exit; } $error = ''; $success = false; if ($_SERVER['REQUEST_METHOD'] == 'POST') { $username = isset($_POST['username']) ? trim($_POST['username']) : ''; $email = isset($_POST['email']) ? trim($_POST['email']) : $invite['email']; $password = isset($_POST['password']) ? $_POST['password'] : ''; $confirm_password = isset($_POST['confirm_password']) ? $_POST['confirm_password'] : ''; if (empty($username) || empty($password) || empty($confirm_password)) { $error = 'All fields are required.'; } elseif ($password != $confirm_password) { $error = 'Passwords do not match.'; } elseif (strlen($password) < 8) { $error = 'Password must be at least 8 characters.'; } else { // Vérifier si l'utilisateur existe déjà $stmt = $db->prepare("SELECT id FROM users WHERE email = ?"); $stmt->execute([$email]); $existing_user = $stmt->fetch(); if ($existing_user) { // L'utilisateur existe déjà, lui connecter à cette famille $user_id = $existing_user['id']; // Vérifier si l'utilisateur est déjà membre de cette famille $stmt = $db->prepare("SELECT id FROM family_members WHERE user_id = ? AND family_id = ?"); $stmt->execute([$user_id, $invite['family_id']]); if ($stmt->rowCount() == 0) { // Ajouter l'utilisateur à la famille $stmt = $db->prepare("INSERT INTO family_members (family_id, user_id, role, joined_at) VALUES (?, ?, 'member', NOW())"); $stmt->execute([$invite['family_id'], $user_id]); // Marquer l'invitation comme acceptée $stmt = $db->prepare("UPDATE family_invites SET accepted = 1, accepted_at = NOW() WHERE id = ?"); $stmt->execute([$invite['id']]); // Connecter l'utilisateur $_SESSION['user_id'] = $user_id; $_SESSION['username'] = $username; $_SESSION['family_id'] = $invite['family_id']; $success = true; } else { $error = 'You are already a member of this family.'; } } else { // Créer un nouvel utilisateur $hashed_password = password_hash($password, PASSWORD_DEFAULT); try { $db->beginTransaction(); // Insérer l'utilisateur $stmt = $db->prepare("INSERT INTO users (username, email, password, created_at) VALUES (?, ?, ?, NOW())"); $stmt->execute([$username, $email, $hashed_password]); $user_id = $db->lastInsertId(); // Ajouter l'utilisateur à la famille $stmt = $db->prepare("INSERT INTO family_members (family_id, user_id, role, joined_at) VALUES (?, ?, 'member', NOW())"); $stmt->execute([$invite['family_id'], $user_id]); // Marquer l'invitation comme acceptée $stmt = $db->prepare("UPDATE family_invites SET accepted = 1, accepted_at = NOW() WHERE id = ?"); $stmt->execute([$invite['id']]); $db->commit(); // Connecter l'utilisateur $_SESSION['user_id'] = $user_id; $_SESSION['username'] = $username; $_SESSION['family_id'] = $invite['family_id']; $success = true; } catch (Exception $e) { $db->rollBack(); $error = 'An error occurred. Please try again.'; // Log l'erreur pour le débogage error_log($e->getMessage()); } } } if ($success) { header('Location: /index.php?page=home'); exit; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Join Family - AndWeare</title> <link rel="icon" type="image/png" href="/assets/images/logo.png"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet"> <script src="https://cdn.tailwindcss.com"></script> <script> tailwind.config = { theme: { extend: { colors: { primary: { 50: '#f0f9ff', 100: '#e0f2fe', 500: '#0ea5e9', 600: '#0284c7', 700: '#0369a1' } } } } } </script> <style> body { font-family: 'Noto Sans KR', sans-serif; background-color: #f0f9ff; } </style> </head> <body class="min-h-screen flex items-center justify-center p-4"> <div class="w-full max-w-md"> <div class="text-center mb-8"> <img src="/assets/images/logo.png" alt="AndWeare Logo" class="h-12 mx-auto mb-4"> <h1 class="text-2xl font-medium text-gray-900">Join the <?php echo htmlspecialchars($invite['family_name']); ?> Family</h1> <p class="text-gray-600 mt-2">Create your account to connect with your family</p> </div> <div class="bg-white rounded-lg shadow-md p-8"> <?php if ($error): ?> <div class="mb-6 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded"> <p><?php echo $error; ?></p> </div> <?php endif; ?> <form method="post" action=""> <div class="mb-4"> <label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email Address</label> <input type="email" id="email" name="email" value="<?php echo htmlspecialchars($invite['email']); ?>" readonly class="w-full px-3 py-2 border border-gray-300 bg-gray-100 rounded-md"> </div> <div class="mb-4"> <label for="username" class="block text-sm font-medium text-gray-700 mb-1">Your Name</label> <input type="text" id="username" name="username" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"> </div> <div class="mb-4"> <label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label> <input type="password" id="password" name="password" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"> </div> <div class="mb-6"> <label for="confirm_password" class="block text-sm font-medium text-gray-700 mb-1">Confirm Password</label> <input type="password" id="confirm_password" name="confirm_password" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"> </div> <button type="submit" class="w-full bg-primary-600 hover:bg-primary-700 text-white font-medium py-2 px-4 rounded-md transition"> Join Family </button> </form> <div class="mt-6 text-center text-sm text-gray-600"> <p>Already have an account? <a href="/index.php" class="text-primary-600 hover:text-primary-700">Sign in</a></p> </div> </div> <div class="mt-6 text-center text-sm text-gray-500"> © <?php echo date('Y'); ?> AndWeare - A product by Imators </div> </div> <script> console.log('Invitation code:', '<?php echo $code; ?>'); console.log('Family name:', '<?php echo addslashes($invite['family_name']); ?>'); console.log('Email:', '<?php echo addslashes($invite['email']); ?>'); </script> </body> </html>PK \8�[�V�`� � app-en/config.phpnu �Iw�� <?php $db_host = 'localhost:3306'; $db_name = 'gqdcvggs_imators-user'; $db_user = 'gqdcvggs'; $db_pass = 'imaors_management.346980*#@-onlyforcpanel;forchange'; try { $db = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); } catch (PDOException $e) { die("Database connection failed: " . $e->getMessage()); } $cdn_url = 'https://cdn.imators.com'; function detectFamily($db) { $host = $_SERVER['HTTP_HOST']; $path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/'); $path_parts = explode('/', $path); if (strpos($host, '.andweare.com') !== false) { // C'est un sous-domaine $family_slug = explode('.', $host)[0]; $site_type = 'subdomain'; } else if ($path_parts[0] && $host === 'andweare.com') { // C'est un dossier $family_slug = $path_parts[0]; $site_type = 'directory'; } else { die("No family found at this URL"); } $stmt = $db->prepare("SELECT * FROM family_sites WHERE family_slug = ?"); $stmt->execute([$family_slug]); $family = $stmt->fetch(); if (!$family) { die("Family not found"); } return $family; } $family = detectFamily($db); $family_id = $family['id']; $site_url = $family['site_url']; function getMembers($db, $family_id) { $stmt = $db->prepare("SELECT u.id, u.username, u.email, fm.role FROM family_members fm JOIN utilisateurs u ON fm.user_id = u.id WHERE fm.family_site_id = ?"); $stmt->execute([$family_id]); return $stmt->fetchAll(); } $members = getMembers($db, $family_id);PK \8�[A�] ] app-en/pages/home.phpnu �Iw�� <div class="bg-white shadow rounded-lg p-6 mb-6"> <h1 class="text-2xl font-bold mb-4"><?php echo htmlspecialchars($family['family_name']); ?> Family Hub</h1> <p class="text-gray-600 mb-4">Welcome to your family's private space. Share photos, track important dates, and stay connected with your loved ones.</p> </div> <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> <div class="bg-white shadow rounded-lg p-6"> <h2 class="text-xl font-bold mb-4 flex items-center"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-2 text-indigo-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /> </svg> Upcoming Events </h2> <div class="divide-y divide-gray-200"> <?php $stmt = $db->prepare("SELECT * FROM events WHERE family_id = ? AND date >= CURDATE() ORDER BY date LIMIT 5"); $stmt->execute([$family_id]); $events = $stmt->fetchAll(); if (count($events) > 0) { foreach ($events as $event) { echo '<div class="py-3">'; echo '<div class="flex justify-between">'; echo '<div class="font-medium">' . htmlspecialchars($event['title']) . '</div>'; echo '<div class="text-sm text-gray-500">' . date('M j, Y', strtotime($event['date'])) . '</div>'; echo '</div>'; if (!empty($event['description'])) { echo '<div class="text-sm text-gray-600 mt-1">' . htmlspecialchars($event['description']) . '</div>'; } echo '</div>'; } } else { echo '<p class="py-3 text-gray-500">No upcoming events. <a href="?page=calendar" class="text-indigo-500 hover:underline">Add one</a>?</p>'; } ?> </div> <div class="mt-4"> <a href="?page=calendar" class="text-indigo-600 hover:text-indigo-800 text-sm font-medium">View all events →</a> </div> </div> <div class="bg-white shadow rounded-lg p-6"> <h2 class="text-xl font-bold mb-4 flex items-center"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-2 text-indigo-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> </svg> Recent Photos </h2> <div class="grid grid-cols-3 gap-2"> <?php $stmt = $db->prepare("SELECT * FROM photos WHERE family_id = ? ORDER BY uploaded_at DESC LIMIT 6"); $stmt->execute([$family_id]); $photos = $stmt->fetchAll(); if (count($photos) > 0) { foreach ($photos as $photo) { echo '<a href="?page=photos&photo=' . $photo['id'] . '" class="aspect-square rounded overflow-hidden">'; echo '<img src="' . htmlspecialchars($cdn_url . '/' . $photo['file_path']) . '" alt="' . htmlspecialchars($photo['title']) . '" class="w-full h-full object-cover hover:opacity-90 transition">'; echo '</a>'; } } else { echo '<div class="col-span-3 text-gray-500 py-3">No photos yet. <a href="?page=photos" class="text-indigo-500 hover:underline">Upload some</a>?</div>'; } ?> </div> <div class="mt-4"> <a href="?page=photos" class="text-indigo-600 hover:text-indigo-800 text-sm font-medium">View all photos →</a> </div> </div> </div> <div class="bg-white shadow rounded-lg p-6"> <h2 class="text-xl font-bold mb-4 flex items-center"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-2 text-indigo-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /> </svg> Family Members </h2> <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"> <?php foreach ($members as $member): ?> <div class="text-center"> <div class="inline-block rounded-full overflow-hidden h-16 w-16 mb-2"> <img src="/assets/images/default-avatar.png" alt="<?php echo htmlspecialchars($member['username']); ?>" class="h-full w-full object-cover"> </div> <div class="font-medium"><?php echo htmlspecialchars($member['username']); ?></div> <div class="text-sm text-gray-500"><?php echo ucfirst(htmlspecialchars($member['role'])); ?></div> </div> <?php endforeach; ?> <?php if ($is_admin): ?> <a href="?page=members&action=invite" class="flex flex-col items-center justify-center p-4 border-2 border-dashed border-gray-300 rounded-lg hover:border-gray-400 transition"> <div class="h-16 w-16 rounded-full flex items-center justify-center bg-gray-100 text-gray-400 mb-2"> <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> </svg> </div> <span class="text-sm font-medium text-indigo-600">Invite Member</span> </a> <?php endif; ?> </div> </div>PK \8�[��rc; c; app-en/pages/members.phpnu �Iw�� <?php $page_title = 'Family Members'; $family_id = $_SESSION['family_id']; $stmt = $db->prepare("SELECT fm.*, u.username, u.email FROM family_members fm JOIN users u ON fm.user_id = u.id WHERE fm.family_id = ? ORDER BY fm.role = 'admin' DESC, u.username"); $stmt->execute([$family_id]); $members = $stmt->fetchAll(); $is_admin = is_admin($db, $family_id); $action = isset($_GET['action']) ? $_GET['action'] : ''; if ($action == 'send_invite' && $_SERVER['REQUEST_METHOD'] == 'POST' && $is_admin) { $email = $_POST['email']; $invite_code = bin2hex(random_bytes(16)); $stmt = $db->prepare("INSERT INTO family_invites (family_id, email, invite_code, invited_by, created_at) VALUES (?, ?, ?, ?, NOW())"); $stmt->execute([$family_id, $email, $invite_code, $_SESSION['user_id']]); $invite_url = "https://" . $_SERVER['HTTP_HOST'] . "/join.php?code=" . $invite_code; $stmt = $db->prepare("SELECT family_name FROM families WHERE id = ?"); $stmt->execute([$family_id]); $family_name = $stmt->fetchColumn(); $stmt = $db->prepare("SELECT username FROM users WHERE id = ?"); $stmt->execute([$_SESSION['user_id']]); $inviter_name = $stmt->fetchColumn(); $subject = "$inviter_name invites you to join their family on AndWeare"; $message = " <html> <head> <title>Family Invitation</title> </head> <body> <div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;'> <div style='background-color: #f0f9ff; padding: 20px; text-align: center;'> <img src='https://" . $_SERVER['HTTP_HOST'] . "/assets/images/logo.png' alt='AndWeare Logo' style='height: 50px;'> </div> <div style='padding: 20px; border: 1px solid #e0f2fe; border-top: none;'> <h2 style='color: #0369a1;'>You've been invited!</h2> <p>Hello,</p> <p><strong>$inviter_name</strong> has invited you to join the <strong>$family_name</strong> family on AndWeare, your private family hub.</p> <p>With AndWeare, you can:</p> <ul> <li>Share family photos privately</li> <li>Keep track of important family events</li> <li>Stay connected with your loved ones</li> </ul> <div style='text-align: center; margin: 30px 0;'> <a href='$invite_url' style='background-color: #0ea5e9; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;'>Accept Invitation</a> </div> <p>Or copy and paste this link into your browser:</p> <p style='font-size: 14px; word-break: break-all; background-color: #f0f9ff; padding: 10px;'>$invite_url</p> <p>This invitation will expire in 7 days.</p> </div> <div style='background-color: #f0f9ff; padding: 15px; text-align: center; font-size: 12px; color: #0369a1;'> © " . date('Y') . " AndWeare - A product by Imators </div> </div> </body> </html> "; $headers = "MIME-Version: 1.0" . "\r\n"; $headers .= "Content-type:text/html;charset=UTF-8" . "\r\n"; $headers .= "From: AndWeare <noreply@andweare.com>" . "\r\n"; mail($email, $subject, $message, $headers); header("Location: ?page=members&invite_sent=1"); exit; } if ($action == 'remove_member' && $is_admin && isset($_GET['id'])) { $member_id = $_GET['id']; $stmt = $db->prepare("SELECT role FROM family_members WHERE id = ?"); $stmt->execute([$member_id]); $member_role = $stmt->fetchColumn(); if ($member_role != 'admin' || count(array_filter($members, function($m) { return $m['role'] == 'admin'; })) > 1) { $stmt = $db->prepare("DELETE FROM family_members WHERE id = ? AND family_id = ?"); $stmt->execute([$member_id, $family_id]); } header("Location: ?page=members"); exit; } if ($action == 'resend_invite' && $is_admin && isset($_GET['id'])) { $invite_id = $_GET['id']; $stmt = $db->prepare("SELECT email, invite_code FROM family_invites WHERE id = ? AND family_id = ?"); $stmt->execute([$invite_id, $family_id]); $invite = $stmt->fetch(); if ($invite) { $invite_url = "https://" . $_SERVER['HTTP_HOST'] . "/join.php?code=" . $invite['invite_code']; $stmt = $db->prepare("SELECT family_name FROM families WHERE id = ?"); $stmt->execute([$family_id]); $family_name = $stmt->fetchColumn(); $stmt = $db->prepare("SELECT username FROM users WHERE id = ?"); $stmt->execute([$_SESSION['user_id']]); $inviter_name = $stmt->fetchColumn(); $subject = "$inviter_name invites you to join their family on AndWeare"; $message = " <html> <head> <title>Family Invitation</title> </head> <body> <div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;'> <div style='background-color: #f0f9ff; padding: 20px; text-align: center;'> <img src='https://" . $_SERVER['HTTP_HOST'] . "/assets/images/logo.png' alt='AndWeare Logo' style='height: 50px;'> </div> <div style='padding: 20px; border: 1px solid #e0f2fe; border-top: none;'> <h2 style='color: #0369a1;'>You've been invited!</h2> <p>Hello,</p> <p><strong>$inviter_name</strong> has invited you to join the <strong>$family_name</strong> family on AndWeare, your private family hub.</p> <p>With AndWeare, you can:</p> <ul> <li>Share family photos privately</li> <li>Keep track of important family events</li> <li>Stay connected with your loved ones</li> </ul> <div style='text-align: center; margin: 30px 0;'> <a href='$invite_url' style='background-color: #0ea5e9; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;'>Accept Invitation</a> </div> <p>Or copy and paste this link into your browser:</p> <p style='font-size: 14px; word-break: break-all; background-color: #f0f9ff; padding: 10px;'>$invite_url</p> <p>This invitation will expire in 7 days.</p> </div> <div style='background-color: #f0f9ff; padding: 15px; text-align: center; font-size: 12px; color: #0369a1;'> © " . date('Y') . " AndWeare - A product by Imators </div> </div> </body> </html> "; $headers = "MIME-Version: 1.0" . "\r\n"; $headers .= "Content-type:text/html;charset=UTF-8" . "\r\n"; $headers .= "From: AndWeare <noreply@andweare.com>" . "\r\n"; mail($invite['email'], $subject, $message, $headers); $stmt = $db->prepare("UPDATE family_invites SET updated_at = NOW() WHERE id = ?"); $stmt->execute([$invite_id]); } header("Location: ?page=members&invite_resent=1"); exit; } if ($action == 'cancel_invite' && $is_admin && isset($_GET['id'])) { $invite_id = $_GET['id']; $stmt = $db->prepare("DELETE FROM family_invites WHERE id = ? AND family_id = ?"); $stmt->execute([$invite_id, $family_id]); header("Location: ?page=members"); exit; } $stmt = $db->prepare("SELECT * FROM family_invites WHERE family_id = ? AND accepted = 0 ORDER BY created_at DESC"); $stmt->execute([$family_id]); $pending_invites = $stmt->fetchAll(); ?> <div class="container mx-auto px-4"> <div class="flex justify-between items-center mb-6"> <h1 class="text-2xl font-bold">Family Members</h1> <?php if ($is_admin): ?> <a href="?page=members&action=invite" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg transition"> Invite New Member </a> <?php endif; ?> </div> <?php if (isset($_GET['invite_sent'])): ?> <div class="mb-6 bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded"> <p>Invitation sent successfully!</p> </div> <?php endif; ?> <?php if (isset($_GET['invite_resent'])): ?> <div class="mb-6 bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded"> <p>Invitation resent successfully!</p> </div> <?php endif; ?> <div class="bg-white shadow rounded-lg overflow-hidden mb-6"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Member</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th> <?php if ($is_admin): ?> <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> <?php endif; ?> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200"> <?php foreach ($members as $member): ?> <tr> <td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden"> <img src="/assets/images/default-avatar.png" alt="<?php echo htmlspecialchars($member['username']); ?>" class="h-full w-full object-cover"> </div> <div class="ml-4"> <div class="text-sm font-medium text-gray-900"><?php echo htmlspecialchars($member['username']); ?></div> </div> </div> </td> <td class="px-6 py-4 whitespace-nowrap"> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?php echo $member['role'] == 'admin' ? 'bg-indigo-100 text-indigo-800' : 'bg-gray-100 text-gray-800'; ?>"> <?php echo ucfirst(htmlspecialchars($member['role'])); ?> </span> </td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <?php echo htmlspecialchars($member['email']); ?> </td> <?php if ($is_admin): ?> <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> <?php if ($member['user_id'] != $_SESSION['user_id']): ?> <a href="?page=members&action=remove_member&id=<?php echo $member['id']; ?>" onclick="return confirm('Are you sure you want to remove this member?')" class="text-red-600 hover:text-red-900">Remove</a> <?php endif; ?> </td> <?php endif; ?> </tr> <?php endforeach; ?> </tbody> </table> </div> <?php if ($is_admin && !empty($pending_invites)): ?> <h2 class="text-xl font-medium mb-4">Pending Invitations</h2> <div class="bg-white shadow rounded-lg overflow-hidden mb-6"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Invited On</th> <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200"> <?php foreach ($pending_invites as $invite): ?> <tr> <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900"> <?php echo htmlspecialchars($invite['email']); ?> </td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <?php echo date('M j, Y', strtotime($invite['created_at'])); ?> </td> <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> <a href="?page=members&action=resend_invite&id=<?php echo $invite['id']; ?>" class="text-indigo-600 hover:text-indigo-900 mr-4">Resend</a> <a href="?page=members&action=cancel_invite&id=<?php echo $invite['id']; ?>" class="text-red-600 hover:text-red-900">Cancel</a> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> <?php endif; ?> </div> <?php if ($action == 'invite' && $is_admin): ?> <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> <div class="bg-white rounded-lg w-full max-w-md p-6"> <div class="flex justify-between items-center mb-4"> <h3 class="text-xl font-medium">Invite New Member</h3> <a href="?page=members" class="text-gray-400 hover:text-gray-500"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </a> </div> <form action="?page=members&action=send_invite" method="post"> <div class="mb-4"> <label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email Address</label> <input type="email" id="email" name="email" required class="w-full px-3 py-2 border border-gray-300 rounded-md"> <p class="mt-1 text-sm text-gray-500">An invitation link will be sent to this email address.</p> </div> <div class="flex justify-end"> <button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg transition">Send Invitation</button> </div> </form> </div> </div> <?php endif; ?>PK \8�[�U�Y app-en/pages/calendar.phpnu �Iw�� <?php $page_title = 'Family Calendar'; $family_id = $_SESSION['family_id']; $month = isset($_GET['month']) ? intval($_GET['month']) : date('n'); $year = isset($_GET['year']) ? intval($_GET['year']) : date('Y'); $timestamp = mktime(0, 0, 0, $month, 1, $year); $days_in_month = date('t', $timestamp); $first_day = date('w', $timestamp); $prev_month = $month == 1 ? 12 : $month - 1; $prev_year = $month == 1 ? $year - 1 : $year; $next_month = $month == 12 ? 1 : $month + 1; $next_year = $month == 12 ? $year + 1 : $year; $stmt = $db->prepare("SELECT * FROM events WHERE family_id = ? AND MONTH(date) = ? AND YEAR(date) = ? ORDER BY date, time"); $stmt->execute([$family_id, $month, $year]); $events = $stmt->fetchAll(); $events_by_day = []; foreach ($events as $event) { $day = intval(date('j', strtotime($event['date']))); if (!isset($events_by_day[$day])) { $events_by_day[$day] = []; } $events_by_day[$day][] = $event; } $action = isset($_GET['action']) ? $_GET['action'] : ''; if ($action == 'add_event' && $_SERVER['REQUEST_METHOD'] == 'POST') { $title = $_POST['title']; $date = $_POST['date']; $time = $_POST['time'] ?: null; $description = $_POST['description'] ?: null; $stmt = $db->prepare("INSERT INTO events (family_id, title, date, time, description, created_by) VALUES (?, ?, ?, ?, ?, ?)"); $stmt->execute([$family_id, $title, $date, $time, $description, $_SESSION['user_id']]); header("Location: ?page=calendar&month=$month&year=$year"); exit; } ?> <div class="container mx-auto px-4"> <div class="flex justify-between items-center mb-6"> <h1 class="text-2xl font-bold">Family Calendar</h1> <a href="?page=calendar&action=create" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg transition"> Add New Event </a> </div> <div class="bg-white shadow rounded-lg overflow-hidden mb-6"> <div class="flex justify-between items-center p-4 border-b"> <a href="?page=calendar&month=<?php echo $prev_month; ?>&year=<?php echo $prev_year; ?>" class="text-gray-600 hover:text-indigo-600 transition"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" /> </svg> </a> <h2 class="text-xl font-medium"><?php echo date('F Y', $timestamp); ?></h2> <a href="?page=calendar&month=<?php echo $next_month; ?>&year=<?php echo $next_year; ?>" class="text-gray-600 hover:text-indigo-600 transition"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> </svg> </a> </div> <div class="grid grid-cols-7 text-center border-b"> <div class="py-2 border-r font-medium">Sun</div> <div class="py-2 border-r font-medium">Mon</div> <div class="py-2 border-r font-medium">Tue</div> <div class="py-2 border-r font-medium">Wed</div> <div class="py-2 border-r font-medium">Thu</div> <div class="py-2 border-r font-medium">Fri</div> <div class="py-2 font-medium">Sat</div> </div> <div class="grid grid-cols-7"> <?php $day_count = 1; $blank_days = $first_day; for ($i = 0; $i < ($days_in_month + $blank_days); $i++) { if ($i < $blank_days) { echo '<div class="min-h-[100px] p-2 border-r border-b bg-gray-50"></div>'; } else { $current_day = $day_count; $is_today = ($day_count == date('j') && $month == date('n') && $year == date('Y')); echo '<div class="min-h-[100px] p-2 border-r border-b relative' . ($is_today ? ' bg-indigo-50' : '') . '">'; echo '<div class="text-right ' . ($is_today ? 'font-bold text-indigo-600' : '') . '">' . $day_count . '</div>'; if (isset($events_by_day[$current_day])) { echo '<div class="mt-1 space-y-1">'; foreach ($events_by_day[$current_day] as $event) { $time_display = $event['time'] ? date('g:i A', strtotime($event['time'])) : ''; echo '<a href="?page=calendar&action=view&id=' . $event['id'] . '" class="block text-xs p-1 bg-indigo-100 text-indigo-800 rounded truncate" title="' . htmlspecialchars($event['title']) . '">'; echo ($time_display ? $time_display . ' - ' : '') . htmlspecialchars($event['title']); echo '</a>'; } echo '</div>'; } echo '</div>'; $day_count++; } } $remaining_cells = 7 - (($days_in_month + $blank_days) % 7); if ($remaining_cells < 7) { for ($i = 0; $i < $remaining_cells; $i++) { echo '<div class="min-h-[100px] p-2 border-r border-b bg-gray-50"></div>'; } } ?> </div> </div> </div> <?php if ($action == 'create'): ?> <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> <div class="bg-white rounded-lg w-full max-w-md p-6"> <div class="flex justify-between items-center mb-4"> <h3 class="text-xl font-medium">Add New Event</h3> <a href="?page=calendar&month=<?php echo $month; ?>&year=<?php echo $year; ?>" class="text-gray-400 hover:text-gray-500"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </a> </div> <form action="?page=calendar&action=add_event&month=<?php echo $month; ?>&year=<?php echo $year; ?>" method="post"> <div class="mb-4"> <label for="title" class="block text-sm font-medium text-gray-700 mb-1">Event Title</label> <input type="text" id="title" name="title" required class="w-full px-3 py-2 border border-gray-300 rounded-md"> </div> <div class="mb-4"> <label for="date" class="block text-sm font-medium text-gray-700 mb-1">Date</label> <input type="date" id="date" name="date" required class="w-full px-3 py-2 border border-gray-300 rounded-md" value="<?php echo date('Y-m-d', $timestamp); ?>"> </div> <div class="mb-4"> <label for="time" class="block text-sm font-medium text-gray-700 mb-1">Time (optional)</label> <input type="time" id="time" name="time" class="w-full px-3 py-2 border border-gray-300 rounded-md"> </div> <div class="mb-4"> <label for="description" class="block text-sm font-medium text-gray-700 mb-1">Description (optional)</label> <textarea id="description" name="description" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md"></textarea> </div> <div class="flex justify-end"> <button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg transition">Add Event</button> </div> </form> </div> </div> <?php endif; ?>PK \8�[��+�ki ki app-en/pages/photos.phpnu �Iw�� <?php $action = isset($_GET['action']) ? $_GET['action'] : 'view'; $album_id = isset($_GET['album']) ? intval($_GET['album']) : null; $photo_id = isset($_GET['photo']) ? intval($_GET['photo']) : null; if ($action === 'upload' && $_SERVER['REQUEST_METHOD'] === 'POST') { if (isset($_FILES['photos']) && !empty($_FILES['photos']['name'][0])) { $album_id = isset($_POST['album_id']) ? intval($_POST['album_id']) : null; $titles = isset($_POST['titles']) ? $_POST['titles'] : []; $descriptions = isset($_POST['descriptions']) ? $_POST['descriptions'] : []; $upload_dir = 'families/' . $family['family_slug'] . '/'; $allowed_types = ['image/jpeg', 'image/png', 'image/gif']; $max_size = 5 * 1024 * 1024; // 5MB $success_count = 0; $errors = []; for ($i = 0; $i < count($_FILES['photos']['name']); $i++) { if ($_FILES['photos']['error'][$i] === UPLOAD_ERR_OK) { $tmp_name = $_FILES['photos']['tmp_name'][$i]; $name = $_FILES['photos']['name'][$i]; $size = $_FILES['photos']['size'][$i]; $type = $_FILES['photos']['type'][$i]; if (!in_array($type, $allowed_types)) { $errors[] = "File type not allowed for {$name}"; continue; } if ($size > $max_size) { $errors[] = "File size too large for {$name}"; continue; } $timestamp = time(); $ext = pathinfo($name, PATHINFO_EXTENSION); $new_filename = $timestamp . '_' . preg_replace('/[^a-zA-Z0-9]/', '', pathinfo($name, PATHINFO_FILENAME)) . '.' . $ext; $destination = $upload_dir . $new_filename; $title = isset($titles[$i]) ? $titles[$i] : ''; $description = isset($descriptions[$i]) ? $descriptions[$i] : ''; $title = $title ?: pathinfo($name, PATHINFO_FILENAME); try { $image = new Imagick($tmp_name); $image->stripImage(); $image->setImageCompressionQuality(85); if ($image->getImageWidth() > 2000 || $image->getImageHeight() > 2000) { $image->resizeImage(2000, 2000, Imagick::FILTER_LANCZOS, 1, true); } $api_url = 'https://cdn.imators.com/api/upload.php'; $data = [ 'api_key' => 'your_api_key', 'file_data' => base64_encode($image->getImageBlob()), 'file_name' => $new_filename, 'destination' => $upload_dir ]; $options = [ 'http' => [ 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => http_build_query($data) ] ]; $context = stream_context_create($options); $result = file_get_contents($api_url, false, $context); $response = json_decode($result, true); if ($response && isset($response['success']) && $response['success']) { $stmt = $db->prepare("INSERT INTO photos (family_id, album_id, title, description, file_path, uploaded_by, uploaded_at) VALUES (?, ?, ?, ?, ?, ?, NOW())"); $stmt->execute([$family_id, $album_id, $title, $description, $destination, $user_id]); $success_count++; } else { $errors[] = "CDN upload failed for {$name}: " . ($response['message'] ?? 'Unknown error'); } } catch (Exception $e) { $errors[] = "Error processing {$name}: " . $e->getMessage(); } } else { $errors[] = "Upload error for file #" . ($i + 1) . ": " . $_FILES['photos']['error'][$i]; } } if ($success_count > 0) { $success_message = "{$success_count} photo" . ($success_count > 1 ? 's' : '') . " uploaded successfully"; } if (count($errors) > 0) { $error_message = implode('<br>', $errors); } if ($album_id) { header("Location: ?page=photos&album={$album_id}"); } else { header("Location: ?page=photos"); } exit; } } if ($action === 'create_album' && $_SERVER['REQUEST_METHOD'] === 'POST') { $album_name = isset($_POST['album_name']) ? trim($_POST['album_name']) : ''; $album_description = isset($_POST['album_description']) ? trim($_POST['album_description']) : ''; if (!empty($album_name)) { $stmt = $db->prepare("INSERT INTO albums (family_id, name, description, created_by, created_at) VALUES (?, ?, ?, ?, NOW())"); $stmt->execute([$family_id, $album_name, $album_description, $user_id]); $new_album_id = $db->lastInsertId(); header("Location: ?page=photos&album={$new_album_id}"); exit; } } if ($action === 'delete_photo' && $is_admin && $photo_id) { $stmt = $db->prepare("SELECT * FROM photos WHERE id = ? AND family_id = ?"); $stmt->execute([$photo_id, $family_id]); $photo = $stmt->fetch(); if ($photo) { $api_url = 'https://cdn.imators.com/api/delete.php'; $data = [ 'api_key' => 'your_api_key', 'file_path' => $photo['file_path'] ]; $options = [ 'http' => [ 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => http_build_query($data) ] ]; $context = stream_context_create($options); file_get_contents($api_url, false, $context); $stmt = $db->prepare("DELETE FROM photos WHERE id = ?"); $stmt->execute([$photo_id]); header("Location: ?page=photos" . ($album_id ? "&album={$album_id}" : "")); exit; } } ?> <div class="mb-6"> <div class="flex items-center justify-between mb-4"> <h1 class="text-2xl font-bold">Family Photos</h1> <div class="flex space-x-2"> <button class="bg-white text-indigo-600 px-4 py-2 rounded-lg border border-indigo-600 hover:bg-indigo-50 transition" onclick="document.getElementById('create-album-modal').classList.remove('hidden')"> New Album </button> <button class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition" onclick="document.getElementById('upload-modal').classList.remove('hidden')"> Upload Photos </button> </div> </div> <?php if ($album_id): ?> <?php $stmt = $db->prepare("SELECT * FROM albums WHERE id = ? AND family_id = ?"); $stmt->execute([$album_id, $family_id]); $album = $stmt->fetch(); if ($album): ?> <div class="mb-6"> <div class="flex items-center justify-between"> <div> <h2 class="text-xl font-bold"><?php echo htmlspecialchars($album['name']); ?></h2> <?php if (!empty($album['description'])): ?> <p class="text-gray-600"><?php echo htmlspecialchars($album['description']); ?></p> <?php endif; ?> </div> <a href="?page=photos" class="text-indigo-600 hover:text-indigo-800">← Back to all photos</a> </div> </div> <?php endif; ?> <?php else: ?> <div class="mb-6"> <h2 class="text-xl font-bold mb-4">Albums</h2> <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"> <?php $stmt = $db->prepare("SELECT a.*, COUNT(p.id) AS photo_count, (SELECT file_path FROM photos WHERE album_id = a.id ORDER BY uploaded_at DESC LIMIT 1) AS cover_photo FROM albums a LEFT JOIN photos p ON a.id = p.album_id AND p.family_id = a.family_id WHERE a.family_id = ? GROUP BY a.id ORDER BY a.created_at DESC"); $stmt->execute([$family_id]); $albums = $stmt->fetchAll(); foreach ($albums as $album): ?> <a href="?page=photos&album=<?php echo $album['id']; ?>" class="bg-white shadow rounded-lg overflow-hidden hover:shadow-md transition"> <div class="aspect-square bg-gray-100"> <?php if ($album['cover_photo']): ?> <img src="<?php echo htmlspecialchars($cdn_url . '/' . $album['cover_photo']); ?>" alt="<?php echo htmlspecialchars($album['name']); ?>" class="w-full h-full object-cover"> <?php else: ?> <div class="w-full h-full flex items-center justify-center text-gray-400"> <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> </svg> </div> <?php endif; ?> </div> <div class="p-4"> <h3 class="font-medium"><?php echo htmlspecialchars($album['name']); ?></h3> <p class="text-sm text-gray-500"><?php echo $album['photo_count']; ?> photo<?php echo $album['photo_count'] != 1 ? 's' : ''; ?></p> </div> </a> <?php endforeach; ?> </div> </div> <h2 class="text-xl font-bold mb-4">All Photos</h2> <?php endif; ?> <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 mb-4"> <?php $query = "SELECT * FROM photos WHERE family_id = ?"; $params = [$family_id]; if ($album_id) { $query .= " AND album_id = ?"; $params[] = $album_id; } $query .= " ORDER BY uploaded_at DESC"; $stmt = $db->prepare($query); $stmt->execute($params); $photos = $stmt->fetchAll(); foreach ($photos as $photo): ?> <div class="bg-white shadow rounded-lg overflow-hidden group"> <a href="#" onclick="openPhotoModal(<?php echo $photo['id']; ?>); return false;" class="block aspect-square"> <img src="<?php echo htmlspecialchars($cdn_url . '/' . $photo['file_path']); ?>" alt="<?php echo htmlspecialchars($photo['title']); ?>" class="w-full h-full object-cover group-hover:opacity-90 transition"> </a> <div class="p-3"> <h3 class="font-medium truncate"><?php echo htmlspecialchars($photo['title']); ?></h3> <div class="flex justify-between items-center mt-1"> <?php $stmt = $db->prepare("SELECT username FROM utilisateurs WHERE id = ?"); $stmt->execute([$photo['uploaded_by']]); $uploader = $stmt->fetch(); ?> <span class="text-xs text-gray-500">By <?php echo htmlspecialchars($uploader['username']); ?></span> <span class="text-xs text-gray-500"><?php echo date('M j, Y', strtotime($photo['uploaded_at'])); ?></span> </div> </div> </div> <?php endforeach; ?> <?php if (count($photos) == 0): ?> <div class="col-span-full py-12 text-center text-gray-500"> <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mx-auto mb-4 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> </svg> <p>No photos yet. Upload some to get started.</p> </div> <?php endif; ?> </div> </div> <!-- Upload Modal --> <div id="upload-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div class="bg-white rounded-lg max-w-2xl w-full max-h-screen overflow-y-auto"> <div class="p-6"> <div class="flex justify-between items-center mb-4"> <h2 class="text-xl font-bold">Upload Photos</h2> <button onclick="document.getElementById('upload-modal').classList.add('hidden')" class="text-gray-500 hover:text-gray-700"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </button> </div> <form action="?page=photos&action=upload" method="POST" enctype="multipart/form-data"> <div class="mb-4"> <label for="album_id" class="block text-sm font-medium text-gray-700 mb-1">Album (optional)</label> <select id="album_id" name="album_id" class="w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"> <option value="">No Album</option> <?php $stmt = $db->prepare("SELECT * FROM albums WHERE family_id = ? ORDER BY name"); $stmt->execute([$family_id]); $albums = $stmt->fetchAll(); foreach ($albums as $album) { $selected = ($album['id'] == $album_id) ? 'selected' : ''; echo "<option value=\"{$album['id']}\" {$selected}>" . htmlspecialchars($album['name']) . "</option>"; } ?> </select> </div> <div class="mb-4"> <label class="block text-sm font-medium text-gray-700 mb-1">Photos</label> <div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-indigo-500 transition cursor-pointer" id="dropzone"> <input type="file" name="photos[]" id="file-input" class="hidden" multiple accept="image/*" onchange="handleFiles(this.files)"> <svg xmlns="http://www.w3.org/2000/svg" class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" /> </svg> <p class="mt-1 text-sm text-gray-600">Drag and drop photos or click to select</p> <p class="mt-1 text-xs text-gray-500">JPEG, PNG, GIF up to 5MB</p> </div> </div> <div id="preview-container" class="mt-4 space-y-4 hidden"> <!-- Preview will be added here by JS --> </div> <div class="flex justify-end mt-6"> <button type="button" onclick="document.getElementById('upload-modal').classList.add('hidden')" class="bg-white px-4 py-2 border border-gray-300 rounded-lg text-gray-700 mr-2 hover:bg-gray-50 transition">Cancel</button> <button type="submit" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition">Upload</button> </div> </form> </div> </div> </div> <!-- Create Album Modal --> <div id="create-album-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div class="bg-white rounded-lg max-w-md w-full"> <div class="p-6"> <div class="flex justify-between items-center mb-4"> <h2 class="text-xl font-bold">Create New Album</h2> <button onclick="document.getElementById('create-album-modal').classList.add('hidden')" class="text-gray-500 hover:text-gray-700"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </button> </div> <form action="?page=photos&action=create_album" method="POST"> <div class="mb-4"> <label for="album_name" class="block text-sm font-medium text-gray-700 mb-1">Album Name</label> <input type="text" id="album_name" name="album_name" required class="w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"> </div> <div class="mb-4"> <label for="album_description" class="block text-sm font-medium text-gray-700 mb-1">Description (optional)</label> <textarea id="album_description" name="album_description" rows="3" class="w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"></textarea> </div> <div class="flex justify-end mt-6"> <button type="button" onclick="document.getElementById('create-album-modal').classList.add('hidden')" class="bg-white px-4 py-2 border border-gray-300 rounded-lg text-gray-700 mr-2 hover:bg-gray-50 transition">Cancel</button> <button type="submit" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition">Create Album</button> </div> </form> </div> </div> </div> <!-- Photo Modal --> <div id="photo-modal" class="fixed inset-0 bg-black bg-opacity-90 flex items-center justify-center z-50 hidden"> <div class="relative max-w-4xl w-full h-full flex flex-col items-center justify-center"> <button onclick="closePhotoModal()" class="absolute top-4 right-4 text-white hover:text-gray-300"> <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </button> <button id="prev-photo" class="absolute left-4 text-white hover:text-gray-300"> <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" /> </svg> </button> <div id="photo-content" class="max-h-[80vh] max-w-full"></div> <button id="next-photo" class="absolute right-4 text-white hover:text-gray-300"> <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> </svg> </button> <div id="photo-info" class="absolute bottom-0 left-0 right-0 bg-black bg-opacity-70 text-white p-4"> <h3 id="photo-title" class="text-lg font-medium"></h3> <p id="photo-description" class="text-sm text-gray-300"></p> <div class="flex justify-between items-center mt-2"> <div> <span id="photo-uploader" class="text-xs text-gray-300"></span> <span id="photo-date" class="text-xs text-gray-300 ml-2"></span> </div> <div> <a id="download-link" href="#" class="text-indigo-300 hover:text-white text-sm mr-4" download>Download</a> <?php if ($is_admin): ?> <a id="delete-link" href="#" class="text-red-300 hover:text-white text-sm" onclick="return confirm('Are you sure you want to delete this photo? This cannot be undone.')">Delete</a> <?php endif; ?> </div> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const dropzone = document.getElementById('dropzone'); const fileInput = document.getElementById('file-input'); dropzone.addEventListener('click', function() { fileInput.click(); }); dropzone.addEventListener('dragover', function(e) { e.preventDefault(); dropzone.classList.add('border-indigo-500'); }); dropzone.addEventListener('dragleave', function() { dropzone.classList.remove('border-indigo-500'); }); dropzone.addEventListener('drop', function(e) { e.preventDefault(); dropzone.classList.remove('border-indigo-500'); if (e.dataTransfer.files.length > 0) { fileInput.files = e.dataTransfer.files; handleFiles(e.dataTransfer.files); } }); }); function handleFiles(files) { const previewContainer = document.getElementById('preview-container'); previewContainer.innerHTML = ''; previewContainer.classList.remove('hidden'); for (let i = 0; i < files.length; i++) { const file = files[i]; if (!file.type.startsWith('image/')) continue; const reader = new FileReader(); reader.onload = function(e) { const preview = document.createElement('div'); preview.className = 'flex items-start p-4 border border-gray-200 rounded-lg'; preview.innerHTML = ` <img src="${e.target.result}" alt="${file.name}" class="w-16 h-16 object-cover rounded mr-4"> <div class="flex-1"> <input type="text" name="titles[]" placeholder="Title" value="${file.name.split('.')[0]}" class="w-full mb-2 border-gray-300 rounded-md shadow-sm focus:border-indigo-500 focus:ring-indigo-500"> <textarea name="descriptions[]" placeholder="Description (optional)" rows="2" class="w-full border-gray-300 rounded-md shadow-sm focus:border-indigo-500 focus:ring-indigo-500"></textarea> </div> `; previewContainer.appendChild(preview); }; reader.readAsDataURL(file); } } let currentPhotoId = null; let photoIds = []; function openPhotoModal(photoId) { currentPhotoId = photoId; loadPhotoData(photoId); document.getElementById('photo-modal').classList.remove('hidden'); // Load photo IDs if not already loaded if (photoIds.length === 0) { const photoElements = document.querySelectorAll('[onclick^="openPhotoModal"]'); photoIds = Array.from(photoElements).map(el => { const match = el.getAttribute('onclick').match(/openPhotoModal\((\d+)\)/); return match ? parseInt(match[1]) : null; }).filter(id => id !== null); } // Set up navigation buttons const currentIndex = photoIds.indexOf(photoId); const prevButton = document.getElementById('prev-photo'); const nextButton = document.getElementById('next-photo'); prevButton.style.display = currentIndex > 0 ? 'block' : 'none'; nextButton.style.display = currentIndex < photoIds.length - 1 ? 'block' : 'none'; prevButton.onclick = function() { if (currentIndex > 0) { openPhotoModal(photoIds[currentIndex - 1]); } }; nextButton.onclick = function() { if (currentIndex < photoIds.length - 1) { openPhotoModal(photoIds[currentIndex + 1]); } }; } function closePhotoModal() { document.getElementById('photo-modal').classList.add('hidden'); } function loadPhotoData(photoId) { fetch(`?page=photos&action=get_photo&photo=${photoId}`) .then(response => response.json()) .then(data => { if (data.success) { const photo = data.photo; document.getElementById('photo-content').innerHTML = `<img src="${photo.url}" alt="${photo.title}" class="max-h-[70vh] max-w-full">`; document.getElementById('photo-title').textContent = photo.title; document.getElementById('photo-description').textContent = photo.description || ''; document.getElementById('photo-uploader').textContent = `Uploaded by ${photo.uploader}`; document.getElementById('photo-date').textContent = photo.date; document.getElementById('download-link').href = photo.url; document.getElementById('download-link').download = photo.title; if (document.getElementById('delete-link')) { document.getElementById('delete-link').href = `?page=photos&action=delete_photo&photo=${photoId}${photo.album_id ? '&album=' + photo.album_id : ''}`; } } }); } document.addEventListener('keydown', function(e) { if (document.getElementById('photo-modal').classList.contains('hidden')) return; if (e.key === 'Escape') { closePhotoModal(); } else if (e.key === 'ArrowLeft') { document.getElementById('prev-photo').click(); } else if (e.key === 'ArrowRight') { document.getElementById('next-photo').click(); } }); </script>PK \8�[;�4Nh h app-en/index.phpnu �Iw�� <?php session_start(); require_once 'config.php'; require_once 'inc/auth.php'; $page = isset($_GET['page']) ? $_GET['page'] : 'home'; $allowed_pages = ['home', 'photos', 'calendar', 'members', 'settings']; if (!in_array($page, $allowed_pages)) { $page = 'home'; } include 'inc/header.php'; include 'pages/' . $page . '.php'; include 'inc/footer.php'; ?>PK \8�[v�4p app-en/inc/header.phpnu �Iw�� <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?php echo htmlspecialchars($family['family_name']); ?> - Family Hub</title> <link rel="icon" type="image/png" href="/assets/images/favicon.png"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet"> <script src="https://cdn.tailwindcss.com"></script> <script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script> </head> <body class="bg-gray-50 font-sans"> <div x-data="{ sidebarOpen: false }"> <nav class="bg-white shadow-sm"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="flex justify-between h-16"> <div class="flex"> <div class="flex-shrink-0 flex items-center"> <button @click="sidebarOpen = !sidebarOpen" class="md:hidden mr-2"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> </svg> </button> <img class="h-8" src="/assets/images/logo.png" alt="Family Hub Logo"> <span class="ml-2 text-xl font-semibold text-gray-900"><?php echo htmlspecialchars($family['family_name']); ?></span> </div> <div class="hidden md:ml-6 md:flex md:space-x-8"> <a href="?page=home" class="<?php echo $page === 'home' ? 'border-b-2 border-indigo-500' : 'border-b-2 border-transparent'; ?> text-gray-900 inline-flex items-center px-1 pt-1 text-sm font-medium">Home</a> <a href="?page=photos" class="<?php echo $page === 'photos' ? 'border-b-2 border-indigo-500' : 'border-b-2 border-transparent'; ?> text-gray-900 inline-flex items-center px-1 pt-1 text-sm font-medium">Photos</a> <a href="?page=calendar" class="<?php echo $page === 'calendar' ? 'border-b-2 border-indigo-500' : 'border-b-2 border-transparent'; ?> text-gray-900 inline-flex items-center px-1 pt-1 text-sm font-medium">Calendar</a> <a href="?page=members" class="<?php echo $page === 'members' ? 'border-b-2 border-indigo-500' : 'border-b-2 border-transparent'; ?> text-gray-900 inline-flex items-center px-1 pt-1 text-sm font-medium">Members</a> </div> </div> <div class="flex items-center"> <div class="hidden md:ml-4 md:flex-shrink-0 md:flex md:items-center"> <div class="relative" x-data="{ open: false }"> <div> <button @click="open = !open" class="flex text-sm rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> <img class="h-8 w-8 rounded-full" src="/assets/images/default-avatar.png" alt=""> </button> </div> <div x-show="open" @click.away="open = false" class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5" style="display: none;"> <a href="?page=settings" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a> <a href="/login/logout.php" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Sign out</a> </div> </div> </div> </div> </div> </div> </nav> <div class="fixed inset-0 flex z-40 md:hidden" x-show="sidebarOpen" x-description="Off-canvas menu for mobile, show/hide based on off-canvas menu state." x-transition:enter="transition-opacity ease-linear duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition-opacity ease-linear duration-300" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" style="display: none;"> <div class="fixed inset-0 bg-gray-600 bg-opacity-75" @click="sidebarOpen = false"></div> <div class="relative flex-1 flex flex-col max-w-xs w-full bg-white"> <div class="absolute top-0 right-0 -mr-12 pt-2"> <button @click="sidebarOpen = false" class="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"> <span class="sr-only">Close sidebar</span> <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </button> </div> <div class="flex-1 h-0 pt-5 pb-4 overflow-y-auto"> <div class="flex-shrink-0 flex items-center px-4"> <img class="h-8" src="/assets/images/logo.png" alt="Family Hub Logo"> <span class="ml-2 text-xl font-semibold text-gray-900"><?php echo htmlspecialchars($family['family_name']); ?></span> </div> <nav class="mt-5 px-2 space-y-1"> <a href="?page=home" class="<?php echo $page === 'home' ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?> group flex items-center px-2 py-2 text-base font-medium rounded-md">Home</a> <a href="?page=photos" class="<?php echo $page === 'photos' ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?> group flex items-center px-2 py-2 text-base font-medium rounded-md">Photos</a> <a href="?page=calendar" class="<?php echo $page === 'calendar' ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?> group flex items-center px-2 py-2 text-base font-medium rounded-md">Calendar</a> <a href="?page=members" class="<?php echo $page === 'members' ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?> group flex items-center px-2 py-2 text-base font-medium rounded-md">Members</a> <a href="?page=settings" class="text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-2 py-2 text-base font-medium rounded-md">Settings</a> <a href="/login/logout.php" class="text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-2 py-2 text-base font-medium rounded-md">Sign out</a> </nav> </div> </div> </div> <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">PK \8�[���� � app-en/inc/auth.phpnu �Iw�� <?php if (!isset($_SESSION['user_id'])) { header('Location: /login'); exit; } $user_id = $_SESSION['user_id']; $stmt = $db->prepare("SELECT fm.* FROM family_members fm WHERE fm.family_site_id = ? AND fm.user_id = ?"); $stmt->execute([$family_id, $user_id]); $current_member = $stmt->fetch(); if (!$current_member) { session_destroy(); header('Location: /login?error=not_member'); exit; } $is_admin = ($current_member['role'] === 'admin'); $current_user = null; if ($user_id) { $stmt = $db->prepare("SELECT id, username, email FROM utilisateurs WHERE id = ?"); $stmt->execute([$user_id]); $current_user = $stmt->fetch(); }PK \8�[��$�� � app-en/inc/footer.phpnu �Iw�� </main> <footer class="bg-white"> <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8 border-t border-gray-200"> <p class="text-center text-sm text-gray-500">© 2025 <?php echo htmlspecialchars($family['family_name']); ?> Family Hub - Powered by <a href="https://andweare.com" class="hover:text-gray-700">AndWeare</a></p> </div> </footer> </div> </body> </html>PK \8�[�b�F F index.htmlnu �Iw�� PK \8�[bU�{ � login/login.phpnu �Iw�� PK \8�[6O}� � .htaccessnu �[��� PK \8�[��t�# �# app-en/join.phpnu �Iw�� PK \8�[�V�`� � /0 app-en/config.phpnu �Iw�� PK \8�[A�] ] <7 app-en/pages/home.phpnu �Iw�� PK \8�[��rc; c; �N app-en/pages/members.phpnu �Iw�� PK \8�[�U�Y �� app-en/pages/calendar.phpnu �Iw�� PK \8�[��+�ki ki � app-en/pages/photos.phpnu �Iw�� PK \8�[;�4Nh h � app-en/index.phpnu �Iw�� PK \8�[v�4p ? app-en/inc/header.phpnu �Iw�� PK \8�[���� � �2 app-en/inc/auth.phpnu �Iw�� PK \8�[��$�� � _5 app-en/inc/footer.phpnu �Iw�� PK , M7
| ver. 1.6 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка