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';
?>