execute($sql)) { return self::$db->insert_id(); } return false; } /** * Get moderation queue * @param string $status Filter by status * @param int $limit Number of items * @return array Queue items */ public static function getQueue($status = 'pending', $limit = 50) { self::init(); $status = VDatabase::escape($status); $limit = (int)$limit; $sql = "SELECT mq.*, au.usr_user as reporter_username FROM db_moderation_queue mq LEFT JOIN db_accountuser au ON mq.reporter_id = au.usr_id WHERE mq.status = '$status' ORDER BY CASE priority WHEN 'urgent' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 ELSE 4 END, mq.created_at ASC LIMIT $limit"; $result = self::$db->execute($sql); $queue = []; if ($result) { while ($row = $result->FetchRow()) { $queue[] = $row; } } return $queue; } /** * Take moderation action * @param int $queue_id Queue item ID * @param int $moderator_id Moderator user ID * @param string $action Action taken * @param string $reason Reason for action * @return bool Success */ public static function takeAction($queue_id, $moderator_id, $action, $reason) { self::init(); $queue_id = (int)$queue_id; $moderator_id = (int)$moderator_id; $action = VDatabase::escape($action); $reason = VDatabase::escape($reason); // Get queue item $sql = "SELECT * FROM db_moderation_queue WHERE queue_id = $queue_id"; $result = self::$db->execute($sql); if (!$result || $result->RecordCount() == 0) { return false; } $item = $result->FetchRow(); // Record action $sql = "INSERT INTO db_moderation_actions (target_type, target_id, moderator_id, action, reason, is_automated, created_at) VALUES ('{$item['target_type']}', '{$item['target_id']}', $moderator_id, '$action', '$reason', 0, NOW())"; if (!self::$db->execute($sql)) { return false; } $action_id = self::$db->insert_id(); // Update queue $sql = "UPDATE db_moderation_queue SET status = 'resolved', resolved_by = $moderator_id, resolution = '$reason', resolved_at = NOW() WHERE queue_id = $queue_id"; self::$db->execute($sql); // Apply action based on type self::applyAction($item['target_type'], $item['target_id'], $action); // Add strike if needed if ($action == 'removed' || $action == 'banned') { self::addStrike($item['target_id'], $action_id, $action, $reason); } return true; } /** * Apply moderation action to target */ private static function applyAction($target_type, $target_id, $action) { switch ($action) { case 'removed': if ($target_type == 'video') { $target_id_safe = VDatabase::escape($target_id); self::$db->execute("UPDATE db_videofiles SET privacy = 'removed', approved = 0 WHERE file_key = '$target_id_safe'"); } break; case 'age_restricted': if ($target_type == 'video') { $target_id_safe = VDatabase::escape($target_id); self::$db->execute("UPDATE db_videofiles SET file_adult = 1 WHERE file_key = '$target_id_safe'"); } break; case 'banned': if ($target_type == 'user') { $usr_id = (int)$target_id; self::$db->execute("UPDATE db_accountuser SET usr_status = 'suspended' WHERE usr_id = $usr_id"); } break; } } /** * Add strike to user */ private static function addStrike($usr_id, $action_id, $type, $reason) { $usr_id = (int)$usr_id; $action_id = (int)$action_id; $type_safe = VDatabase::escape($type); $reason_safe = VDatabase::escape($reason); // Determine strike type based on severity $strike_type = match($type) { 'removed' => 'strike', 'banned' => 'ban', default => 'warning' }; $sql = "INSERT INTO db_user_strikes (usr_id, action_id, type, reason, is_active, created_at) VALUES ($usr_id, $action_id, '$strike_type', '$reason_safe', 1, NOW())"; return self::$db->execute($sql); } /** * Get user strikes */ public static function getUserStrikes($usr_id) { self::init(); $usr_id = (int)$usr_id; $sql = "SELECT * FROM db_user_strikes WHERE usr_id = $usr_id AND is_active = 1 ORDER BY created_at DESC"; $result = self::$db->execute($sql); $strikes = []; if ($result) { while ($row = $result->FetchRow()) { $strikes[] = $row; } } return $strikes; } /** * Submit appeal */ public static function submitAppeal($action_id, $usr_id, $reason, $evidence = []) { self::init(); $action_id = (int)$action_id; $usr_id = (int)$usr_id; $reason_safe = VDatabase::escape($reason); $evidence_json = VDatabase::escape(json_encode($evidence)); $sql = "INSERT INTO db_moderation_appeals (action_id, usr_id, reason, evidence, status, created_at) VALUES ($action_id, $usr_id, '$reason_safe', '$evidence_json', 'pending', NOW())"; if (self::$db->execute($sql)) { // Mark action as appealed self::$db->execute("UPDATE db_moderation_actions SET is_appealed = 1 WHERE action_id = $action_id"); return self::$db->insert_id(); } return false; } /** * Get pending appeals */ public static function getPendingAppeals($limit = 50) { self::init(); $limit = (int)$limit; $sql = "SELECT ma.*, au.usr_user, au.usr_email FROM db_moderation_appeals ma JOIN db_accountuser au ON ma.usr_id = au.usr_id WHERE ma.status = 'pending' ORDER BY ma.created_at ASC LIMIT $limit"; $result = self::$db->execute($sql); $appeals = []; if ($result) { while ($row = $result->FetchRow()) { $row['evidence'] = json_decode($row['evidence'], true); $appeals[] = $row; } } return $appeals; } /** * Review appeal */ public static function reviewAppeal($appeal_id, $reviewer_id, $status, $notes) { self::init(); $appeal_id = (int)$appeal_id; $reviewer_id = (int)$reviewer_id; $status_safe = VDatabase::escape($status); $notes_safe = VDatabase::escape($notes); $sql = "UPDATE db_moderation_appeals SET status = '$status_safe', reviewed_by = $reviewer_id, review_notes = '$notes_safe', reviewed_at = NOW() WHERE appeal_id = $appeal_id"; if (!self::$db->execute($sql)) { return false; } // If approved, restore content if ($status == 'approved') { $sql = "SELECT action_id FROM db_moderation_appeals WHERE appeal_id = $appeal_id"; $result = self::$db->execute($sql); if ($result) { $row = $result->FetchRow(); $action_id = (int)$row['action_id']; // Get original action $sql = "SELECT * FROM db_moderation_actions WHERE action_id = $action_id"; $result = self::$db->execute($sql); if ($result) { $action = $result->FetchRow(); self::restoreContent($action['target_type'], $action['target_id']); } } } return true; } /** * Restore content after successful appeal */ private static function restoreContent($target_type, $target_id) { if ($target_type == 'video') { $target_id_safe = VDatabase::escape($target_id); self::$db->execute("UPDATE db_videofiles SET privacy = 'public', approved = 1 WHERE file_key = '$target_id_safe'"); } } }