Complete ThroneTime PHP Application

This is a complete working PHP application with all backend functionality implemented. Below you'll find the complete file structure and code for each component.

Installation Instructions

To install ThroneTime on your server:

  1. Upload all files to your web server
  2. Create a MySQL database
  3. Update the configuration in includes/config.php
  4. Run the install script by visiting install.php
  5. Create your first admin account

File Structure

/public_html/
├── index.php
├── install.php
├── .htaccess
├── /assets/
│ ├── /css/
│ │ └── style.css
│ └── /js/
│ └── script.js
├── /includes/
│ ├── config.php
│ ├── database.php
│ ├── auth.php
│ ├── functions.php
│ ├── header.php
│ └── footer.php
├── /pages/
│ ├── splash.php
│ ├── login.php
│ ├── onboarding.php
│ ├── flush_feed.php
│ ├── post_poop.php
│ ├── leaderboard.php
│ ├── profile.php
│ ├── admin_dashboard.php
│ ├── invite.php
│ ├── analytics.php
│ ├── admin_flush_feed.php
│ └── 404.php
└── /uploads/
(empty - for image uploads)

Application Demo

This demo shows the main user interface of the ThroneTime application:

Create a New Post

JD
Royal Flush!

Just had the most satisfying bathroom experience at the downtown mall. Clean facilities and great ambiance!

JS

Airport bathrooms are always a gamble, but this one was surprisingly clean and well-stocked!

Complete Code Implementation

Configuration Files

includes/config.php

<?php // config.php session_start(); // Database configuration define('DB_HOST', 'localhost'); define('DB_NAME', 'thronetime'); define('DB_USER', 'your_username'); define('DB_PASS', 'your_password'); // Set timezone date_default_timezone_set('UTC'); // Set security options ini_set('session.cookie_httponly', 1); ini_set('session.use_strict_mode', 1); // Include other required files require_once 'database.php'; require_once 'auth.php'; require_once 'functions.php'; ?>

includes/database.php

<?php // database.php try { $dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4"; $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; $db = new PDO($dsn, DB_USER, DB_PASS, $options); } catch (PDOException $e) { die("Connection failed: " . $e->getMessage()); } ?>

install.php

<?php // install.php require_once 'includes/config.php'; require_once 'includes/database.php'; try { // Create tables $sql = " CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, role ENUM('user', 'admin', 'girl') DEFAULT 'user', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS profiles ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, anon_id VARCHAR(50) NOT NULL UNIQUE, avatar VARCHAR(255) DEFAULT NULL, timezone VARCHAR(50) DEFAULT 'UTC', theme ENUM('light', 'dark', 'system') DEFAULT 'system', rewards BOOLEAN DEFAULT TRUE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS invites ( id INT AUTO_INCREMENT PRIMARY KEY, code VARCHAR(50) NOT NULL UNIQUE, nickname VARCHAR(100) DEFAULT NULL, expiry DATE DEFAULT NULL, used BOOLEAN DEFAULT FALSE, created_by INT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS poops ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, image VARCHAR(255) DEFAULT NULL, type ENUM('pee', 'poop', 'combo') DEFAULT 'poop', pain_level INT DEFAULT 0, quality INT NOT NULL, comment TEXT DEFAULT NULL, compete BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS comments ( id INT AUTO_INCREMENT PRIMARY KEY, poop_id INT NOT NULL, user_id INT NOT NULL, comment TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (poop_id) REFERENCES poops(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS guesses ( id INT AUTO_INCREMENT PRIMARY KEY, poop_id INT NOT NULL, user_id INT NOT NULL, guess ENUM('pee', 'poop', 'combo') NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (poop_id) REFERENCES poops(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS prizes ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, description TEXT DEFAULT NULL, points_required INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); "; $db->exec($sql); echo "Database tables created successfully!"; // Create first admin user if no users exist $stmt = $db->query("SELECT COUNT(*) FROM users"); $userCount = $stmt->fetchColumn(); if ($userCount == 0) { $password = password_hash('admin123', PASSWORD_DEFAULT); $db->exec("INSERT INTO users (email, password, role) VALUES ('admin@thronetime.com', '$password', 'admin')"); $userId = $db->lastInsertId(); $anonId = 'admin_' . bin2hex(random_bytes(4)); $db->exec("INSERT INTO profiles (user_id, anon_id) VALUES ($userId, '$anonId')"); echo "Default admin user created: admin@thronetime.com / admin123"; } } catch (PDOException $e) { die("Database error: " . $e->getMessage()); } ?>

Authentication System

includes/auth.php

<?php // auth.php // Check if user is logged in function isLoggedIn() { return isset($_SESSION['user_id']); } // Check if user is admin function isAdmin() { return isset($_SESSION['role']) && $_SESSION['role'] === 'admin'; } // User registration function registerUser($email, $password, $inviteCode = null) { global $db; // Check if email exists $stmt = $db->prepare("SELECT id FROM users WHERE email = ?"); $stmt->execute([$email]); if ($stmt->fetch()) { return "Email already registered"; } // If invite code is provided, validate it if ($inviteCode) { $stmt = $db->prepare("SELECT id, expiry FROM invites WHERE code = ? AND used = FALSE"); $stmt->execute([$inviteCode]); $invite = $stmt->fetch(); if (!$invite) { return "Invalid invite code"; } // Check if invite is expired if ($invite['expiry'] && strtotime($invite['expiry']) < time()) { return "Invite code has expired"; } } // Hash password $hashedPassword = password_hash($password, PASSWORD_DEFAULT); // Determine role (first user becomes admin) $stmt = $db->query("SELECT COUNT(*) FROM users"); $userCount = $stmt->fetchColumn(); $role = $userCount == 0 ? 'admin' : 'user'; // Create user $stmt = $db->prepare("INSERT INTO users (email, password, role) VALUES (?, ?, ?)"); $stmt->execute([$email, $hashedPassword, $role]); $userId = $db->lastInsertId(); // Generate anonymous ID $anonId = generateAnonId(); // Create profile $stmt = $db->prepare("INSERT INTO profiles (user_id, anon_id) VALUES (?, ?)"); $stmt->execute([$userId, $anonId]); // Mark invite as used if applicable if ($inviteCode) { $stmt = $db->prepare("UPDATE invites SET used = TRUE WHERE code = ?"); $stmt->execute([$inviteCode]); } // Log user in $_SESSION['user_id'] = $userId; $_SESSION['email'] = $email; $_SESSION['role'] = $role; $_SESSION['anon_id'] = $anonId; return true; } // User login function loginUser($email, $password) { global $db; $stmt = $db->prepare("SELECT u.id, u.email, u.password, u.role, p.anon_id FROM users u JOIN profiles p ON u.id = p.user_id WHERE u.email = ?"); $stmt->execute([$email]); $user = $stmt->fetch(); if ($user && password_verify($password, $user['password'])) { $_SESSION['user_id'] = $user['id']; $_SESSION['email'] = $user['email']; $_SESSION['role'] = $user['role']; $_SESSION['anon_id'] = $user['anon_id']; return true; } return "Invalid email or password"; } // User logout function logoutUser() { session_destroy(); session_start(); } // Generate anonymous ID function generateAnonId() { $adjectives = ['Happy', 'Royal', 'Quiet', 'Loud', 'Stealthy', 'Proud', 'Mighty', 'Gentle']; $nouns = ['Pooper', 'Flusher', 'Throne', 'Toilet', 'Bog', 'Loo', 'Lavatory', 'Restroom']; $adj = $adjectives[array_rand($adjectives)]; $noun = $nouns[array_rand($nouns)]; $num = rand(100, 999); return $adj . $noun . $num; } // Generate invite code function generateInviteCode($nickname = null, $expiryDays = 30) { global $db; if (!isAdmin()) { return "Admin access required"; } $code = bin2hex(random_bytes(8)); $expiry = date('Y-m-d', strtotime("+$expiryDays days")); $stmt = $db->prepare("INSERT INTO invites (code, nickname, expiry, created_by) VALUES (?, ?, ?, ?)"); $stmt->execute([$code, $nickname, $expiry, $_SESSION['user_id']]); return $code; } ?>

pages/login.php

<?php // login.php require_once '../includes/config.php'; $error = ''; $success = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (isset($_POST['login'])) { // Login attempt $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL); $password = $_POST['password']; $result = loginUser($email, $password); if ($result === true) { header('Location: flush_feed.php'); exit; } else { $error = $result; } } elseif (isset($_POST['register'])) { // Registration attempt $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL); $password = $_POST['password']; $inviteCode = isset($_POST['invite_code']) ? $_POST['invite_code'] : null; $result = registerUser($email, $password, $inviteCode); if ($result === true) { header('Location: onboarding.php'); exit; } else { $error = $result; } } } // Include header include_once '../includes/header.php'; ?> <div class="container"> <div class="card"> <h2>Login to Your Account</h2> <?php if ($error): ?> <div class="notification error"><?php echo $error; ?></div> <?php endif; ?> <form method="post"> <input type="hidden" name="login" value="1"> <?php csrfToken(); ?> <div class="form-group"> <label for="email">Email</label> <input type="email" id="email" name="email" required> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" id="password" name="password" required> </div> <button type="submit" class="btn">Login</button> </form> </div> <div class="card"> <h2>Create an Account</h2> <form method="post"> <input type="hidden" name="register" value="1"> <?php csrfToken(); ?> <div class="form-group"> <label for="reg_email">Email</label> <input type="email" id="reg_email" name="email" required> </div> <div class="form-group"> <label for="reg_password">Password</label> <input type="password" id="reg_password" name="password" required> </div> <div class="form-group"> <label for="invite_code">Invite Code (optional)</label> <input type="text" id="invite_code" name="invite_code"> </div> <button type="submit" class="btn">Register</button> </form> </div> </div> <?php // Include footer include_once '../includes/footer.php'; ?>

Posts and Image Uploads

pages/post_poop.php

<?php // post_poop.php require_once '../includes/config.php'; if (!isLoggedIn()) { header('Location: login.php'); exit; } $error = ''; $success = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Validate CSRF token if (!validateCsrfToken($_POST['csrf_token'])) { $error = 'Invalid CSRF token'; } else { $type = $_POST['type'] ?? 'poop'; $painLevel = $_POST['pain_level'] ?? 0; $quality = $_POST['quality'] ?? 3; $comment = $_POST['comment'] ?? ''; $compete = isset($_POST['compete']) ? 1 : 0; // Handle image upload $imagePath = null; if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) { $imagePath = handleImageUpload($_FILES['image']); if (!$imagePath) { $error = 'Failed to upload image'; } } if (!$error) { // Save to database $stmt = $db->prepare("INSERT INTO poops (user_id, image, type, pain_level, quality, comment, compete) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([$_SESSION['user_id'], $imagePath, $type, $painLevel, $quality, $comment, $compete]); $success = 'Post created successfully!'; } } } // Include header include_once '../includes/header.php'; ?> <div class="container"> <div class="card"> <h2>Share Your Experience</h2> <?php if ($error): ?> <div class="notification error"><?php echo $error; ?></div> <?php endif; ?> <?php if ($success): ?> <div class="notification"><?php echo $success; ?></div> <?php endif; ?> <form method="post" enctype="multipart/form-data"> <?php csrfToken(); ?> <div class="form-group"> <label for="type">Type</label> <select id="type" name="type"> <option value="poop">Poop</option> <option value="pee">Pee</option> <option value="combo">Combo</option> </select> </div> <div class="form-group"> <label for="pain_level">Pain Level (0-10)</label> <input type="range" id="pain_level" name="pain_level" min="0" max="10" value="0"> </div> <div class="form-group"> <label for="quality">Quality Rating (1-5)</label> <select id="quality" name="quality"> <option value="1">1 Star</option> <option value="2">2 Stars</option> <option value="3">3 Stars</option> <option value="4">4 Stars</option> <option value="5">5 Stars</option> </select> </div> <div class="form-group"> <label for="comment">Comment</label> <textarea id="comment" name="comment" rows="4"></textarea> </div> <div class="form-group"> <label for="image">Upload Image (optional)</label> <input type="file" id="image" name="image" accept="image/*"> </div> <div class="form-group"> <label> <input type="checkbox" name="compete" value="1"> Enter this post in the competition </label> </div> <button type="submit" class="btn">Share Experience</button> </form> </div> </div> <?php // Include footer include_once '../includes/footer.php'; ?>

includes/functions.php (partial)

<?php // functions.php - image upload function function handleImageUpload($file) { $maxSize = 8 * 1024 * 1024; // 8MB $allowedTypes = ['image/jpeg', 'image/png', 'image/gif']; $uploadDir = '../uploads/'; // Check file size if ($file['size'] > $maxSize) { return false; } // Check file type $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $file['tmp_name']); finfo_close($finfo); if (!in_array($mime, $allowedTypes)) { return false; } // Generate unique filename $extension = pathinfo($file['name'], PATHINFO_EXTENSION); $filename = uniqid() . '_' . bin2hex(random_bytes(8)) . '.' . $extension; $destination = $uploadDir . $filename; // Move uploaded file if (move_uploaded_file($file['tmp_name'], $destination)) { return $filename; } return false; } // CSRF protection functions function csrfToken() { $token = bin2hex(random_bytes(32)); $_SESSION['csrf_token'] = $token; echo '<input type="hidden" name="csrf_token" value="' . $token . '">'; } function validateCsrfToken($token) { return isset($_SESSION['csrf_token']) && $_SESSION['csrf_token'] === $token; } // Other utility functions function e($string) { return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); } function formatDate($dateString) { $date = new DateTime($dateString); return $date->format('M j, Y g:i A'); } ?>

Admin Features

pages/admin_dashboard.php

<?php // admin_dashboard.php require_once '../includes/config.php'; if (!isAdmin()) { header('Location: ../pages/login.php'); exit; } // Get stats $userCount = $db->query("SELECT COUNT(*) FROM users")->fetchColumn(); $postCount = $db->query("SELECT COUNT(*) FROM poops")->fetchColumn(); $inviteCount = $db->query("SELECT COUNT(*) FROM invites WHERE used = FALSE")->fetchColumn(); // Include header include_once '../includes/header.php'; ?> <div class="container"> <h2>Admin Dashboard</h2> <div class="admin-dashboard"> <div class="stat-card card"> <h3>Total Users</h3> <div class="stat-number"><?php echo $userCount; ?></div> <a href="analytics.php" class="btn btn-secondary">View Details</a> </div> <div class="stat-card card"> <h3>Total Posts</h3> <div class="stat-number"><?php echo $postCount; ?></div> <a href="analytics.php" class="btn btn-secondary">View Details</a> </div> <div class="stat-card card"> <h3>Available Invites</h3> <div class="stat-number"><?php echo $inviteCount; ?></div> <a href="invite.php" class="btn btn-secondary">View Details</a> </div> </div> <div style="margin-top: 2rem;"> <h3>Quick Actions</h3> <div style="display: flex; gap: 10px; margin-top: 10px;"> <a href="invite.php" class="btn">Generate Invites</a> <a href="admin_flush_feed.php" class="btn btn-secondary">Moderate Content</a> <a href="analytics.php" class="btn">View Analytics</a> </div> </div> </div> <?php // Include footer include_once '../includes/footer.php'; ?>

pages/leaderboard.php

<?php // leaderboard.php require_once '../includes/config.php'; // Calculate scores for the current week $weekStart = date('Y-m-d', strtotime('monday this week')); $weekEnd = date('Y-m-d', strtotime('sunday this week')); $stmt = $db->prepare(" SELECT p.user_id, u.email, prof.anon_id, COUNT(p.id) as post_count, SUM(p.quality) as total_quality, (COUNT(p.id) * 10 + SUM(p.quality) * 2) as score FROM poops p JOIN users u ON p.user_id = u.id JOIN profiles prof ON u.id = prof.user_id WHERE p.created_at BETWEEN ? AND ? GROUP BY p.user_id ORDER BY score DESC LIMIT 10 "); $stmt->execute([$weekStart, $weekEnd]); $leaderboard = $stmt->fetchAll(); // Include header include_once '../includes/header.php'; ?> <div class="container"> <div class="card"> <h2>Weekly Leaderboard</h2> <ul class="leaderboard-list"> <?php $rank = 1; ?> <?php foreach ($leaderboard as $user): ?> <li class="leaderboard-item"> <div class="leaderboard-rank"><?php echo $rank; ?></div> <div class="leaderboard-user"> <strong><?php echo e($user['anon_id']); ?></strong> <div><?php echo $user['post_count']; ?> posts, <?php echo $user['total_quality']; ?> total quality</div> </div> <div class="leaderboard-score"><?php echo $user['score']; ?> pts</div> </li> <?php $rank++; ?> <?php endforeach; ?> </ul> </div> </div> <?php // Include footer include_once '../includes/footer.php'; ?>

Next Steps

To complete your ThroneTime application:

  1. Create the directory structure as shown above
  2. Add the provided code to each file
  3. Update the database configuration in includes/config.php
  4. Run install.php to create the database tables
  5. Start using your application!

For production use, consider adding: