diff --git a/application/_installation/04-create-table-messages.sql b/application/_installation/04-create-table-messages.sql new file mode 100644 index 0000000..74c3129 --- /dev/null +++ b/application/_installation/04-create-table-messages.sql @@ -0,0 +1,20 @@ +CREATE TABLE IF NOT EXISTS `messages` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `sender_id` int(11) NOT NULL, + `receiver_id` int(11) DEFAULT NULL, + `group_type` enum('admins','moderators','all_users') DEFAULT NULL, + `subject` varchar(255) NOT NULL, + `message` text NOT NULL, + `is_read` tinyint(1) NOT NULL DEFAULT 0, + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `sender_id` (`sender_id`), + KEY `receiver_id` (`receiver_id`), + KEY `is_read` (`is_read`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- Foreign key constraints +ALTER TABLE `messages` + ADD CONSTRAINT `messages_ibfk_1` FOREIGN KEY (`sender_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE, + ADD CONSTRAINT `messages_ibfk_2` FOREIGN KEY (`receiver_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE; \ No newline at end of file diff --git a/application/controller/MessageController.php b/application/controller/MessageController.php new file mode 100644 index 0000000..4762a42 --- /dev/null +++ b/application/controller/MessageController.php @@ -0,0 +1,212 @@ + false, 'message' => 'Missing parameters. Use: message/send/{receiver_id}/{subject}/{message}']); + return; + } + + // Verify receiver exists + $receiver = UserModel::getPublicProfileOfUser($receiver_id); + if (!$receiver) { + header('Content-Type: application/json'); + echo json_encode(['success' => false, 'message' => 'Receiver not found']); + return; + } + + // Send the message + $sender_id = Session::get('user_id'); + $success = MessageModel::sendToUser($sender_id, $receiver_id, $subject, $message); + + header('Content-Type: application/json'); + if ($success) { + echo json_encode(['success' => true, 'message' => 'Message sent successfully']); + } else { + echo json_encode(['success' => false, 'message' => 'Failed to send message']); + } + } + + /** + * Send a message to a group via URL parameters + * URL format: message/sendgroup/{group_type}/{subject}/{message} + * group_type can be: admins, moderators, all_users + */ + public function sendgroup() + { + // Handle POST request + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $group_type = isset($_POST['group_type']) ? $_POST['group_type'] : null; + $subject = isset($_POST['subject']) ? $_POST['subject'] : 'No Subject'; + $message = isset($_POST['message']) ? $_POST['message'] : null; + + if (!$group_type || !$message) { + Session::add('feedback_negative', 'Group type and message are required'); + Redirect::to('message'); + return; + } + + // Validate group type + if (!in_array($group_type, ['admins', 'moderators', 'all_users'])) { + Session::add('feedback_negative', 'Invalid group type'); + Redirect::to('message'); + return; + } + + // Send the message + $sender_id = Session::get('user_id'); + $success = MessageModel::sendToGroup($sender_id, $group_type, $subject, $message); + + if ($success) { + Session::add('feedback_positive', 'Message sent to group successfully'); + } else { + Session::add('feedback_negative', 'Failed to send message to group'); + } + + Redirect::to('message'); + return; + } + + // Handle GET request + $url_parts = explode('/', trim($_SERVER['REQUEST_URI'], '/')); + $group_type = isset($url_parts[2]) ? $url_parts[2] : null; + $subject = isset($url_parts[3]) ? urldecode($url_parts[3]) : null; + $message = isset($url_parts[4]) ? urldecode($url_parts[4]) : null; + + if (!$group_type || !$subject || !$message) { + header('Content-Type: application/json'); + echo json_encode(['success' => false, 'message' => 'Missing parameters. Use: message/sendgroup/{group_type}/{subject}/{message}']); + return; + } + + // Validate group type + if (!in_array($group_type, ['admins', 'moderators', 'all_users'])) { + header('Content-Type: application/json'); + echo json_encode(['success' => false, 'message' => 'Invalid group type. Must be: admins, moderators, or all_users']); + return; + } + + // Send the message + $sender_id = Session::get('user_id'); + $success = MessageModel::sendToGroup($sender_id, $group_type, $subject, $message); + + header('Content-Type: application/json'); + if ($success) { + echo json_encode(['success' => true, 'message' => 'Message sent to group successfully']); + } else { + echo json_encode(['success' => false, 'message' => 'Failed to send message to group']); + } + } + + /** + * Show the messenger interface + */ + public function index() + { + $user_id = Session::get('user_id'); + + // Get conversations and unread count + $conversations = MessageModel::getConversations($user_id); + $unread_count = MessageModel::getUnreadCount($user_id); + + $this->View->render('message/index', array( + 'conversations' => $conversations, + 'unread_count' => $unread_count, + 'all_users' => MessageModel::getAllUsers($user_id) + )); + } + + /** + * Show conversation with a specific user + */ + public function conversation() + { + $user_id = Session::get('user_id'); + $url_parts = explode('/', trim($_SERVER['REQUEST_URI'], '/')); + $other_user_id = isset($url_parts[2]) ? $url_parts[2] : null; + + if (!$other_user_id) { + Redirect::to('message'); + return; + } + + // Get user info for the other person + $other_user = UserModel::getPublicProfileOfUser($other_user_id); + if (!$other_user) { + Redirect::to('message'); + return; + } + + // Get messages + $messages = MessageModel::getMessagesWithUser($user_id, $other_user_id); + + $this->View->render('message/conversation', array( + 'messages' => $messages, + 'other_user' => $other_user + )); + } + + /** + * Get unread count as JSON + */ + public function unreadcount() + { + $user_id = Session::get('user_id'); + $unread_count = MessageModel::getUnreadCount($user_id); + + header('Content-Type: application/json'); + echo json_encode(['unread_count' => $unread_count]); + } +} \ No newline at end of file diff --git a/application/controller/ProfileController.php b/application/controller/ProfileController.php index 3cff93e..c844932 100644 --- a/application/controller/ProfileController.php +++ b/application/controller/ProfileController.php @@ -16,8 +16,16 @@ class ProfileController extends Controller */ public function index() { + $all_users = UserModel::getPublicProfilesOfAllUsers(); + + // Remove current user from the list + $current_user_id = Session::get('user_id'); + if ($current_user_id && isset($all_users[$current_user_id])) { + unset($all_users[$current_user_id]); + } + $this->View->render('profile/index', array( - 'users' => UserModel::getPublicProfilesOfAllUsers()) + 'users' => $all_users) ); } diff --git a/application/model/MessageModel.php b/application/model/MessageModel.php new file mode 100644 index 0000000..6ecef85 --- /dev/null +++ b/application/model/MessageModel.php @@ -0,0 +1,186 @@ +getConnection(); + + $sql = "INSERT INTO messages (sender_id, receiver_id, group_type, subject, message) + VALUES (:sender_id, :receiver_id, :group_type, :subject, :message)"; + $query = $database->prepare($sql); + return $query->execute(array( + ':sender_id' => $sender_id, + ':receiver_id' => $receiver_id, + ':group_type' => $group_type, + ':subject' => $subject, + ':message' => $message + )); + } + + public static function sendToUser($sender_id, $receiver_id, $subject, $message) + { + return self::sendMessage($sender_id, $receiver_id, null, $subject, $message); + } + + public static function sendToGroup($sender_id, $group_type, $subject, $message) + { + return self::sendMessage($sender_id, null, $group_type, $subject, $message); + } + + public static function getUnreadCount($user_id) + { + $database = DatabaseFactory::getFactory()->getConnection(); + + $sql = "SELECT COUNT(*) as count FROM messages + WHERE receiver_id = :user_id AND is_read = 0"; + $query = $database->prepare($sql); + $query->execute(array(':user_id' => $user_id)); + $result = $query->fetch(); + + return $result->count; + } + + public static function getUnreadCountForUser($user_id, $sender_id) + { + $database = DatabaseFactory::getFactory()->getConnection(); + + $sql = "SELECT COUNT(*) as count FROM messages + WHERE receiver_id = :user_id AND sender_id = :sender_id AND is_read = 0"; + $query = $database->prepare($sql); + $query->execute(array( + ':user_id' => $user_id, + ':sender_id' => $sender_id + )); + $result = $query->fetch(); + + return $result->count; + } + + public static function getConversations($user_id) + { + $database = DatabaseFactory::getFactory()->getConnection(); + + $sql = "SELECT DISTINCT + CASE + WHEN sender_id = :user_id THEN receiver_id + ELSE sender_id + END as other_user_id, + u.user_name, + u.user_email, + MAX(m.created_at) as last_message_time, + (SELECT COUNT(*) FROM messages m2 + WHERE ((m2.sender_id = :user_id AND m2.receiver_id = other_user_id) OR + (m2.receiver_id = :user_id AND m2.sender_id = other_user_id)) + AND m2.is_read = 0 AND m2.receiver_id = :user_id) as unread_count + FROM messages m + JOIN users u ON (CASE + WHEN sender_id = :user_id THEN receiver_id + ELSE sender_id + END) = u.user_id + WHERE (sender_id = :user_id OR receiver_id = :user_id) + AND receiver_id IS NOT NULL + GROUP BY other_user_id + ORDER BY last_message_time DESC"; + + $query = $database->prepare($sql); + $query->execute(array(':user_id' => $user_id)); + return $query->fetchAll(); + } + + public static function getMessagesWithUser($user_id, $other_user_id) + { + $database = DatabaseFactory::getFactory()->getConnection(); + + $sql = "SELECT m.*, u_sender.user_name as sender_name, + CASE + WHEN m.sender_id = :user_id THEN 'sent' + ELSE 'received' + END as message_type + FROM messages m + JOIN users u_sender ON m.sender_id = u_sender.user_id + WHERE ((m.sender_id = :user_id AND m.receiver_id = :other_user_id) OR + (m.receiver_id = :user_id AND m.sender_id = :other_user_id)) + AND m.group_type IS NULL + ORDER BY m.created_at ASC"; + + $query = $database->prepare($sql); + $query->execute(array( + ':user_id' => $user_id, + ':other_user_id' => $other_user_id + )); + $messages = $query->fetchAll(); + + // Mark received messages as read + self::markAsRead($user_id, $other_user_id); + + return $messages; + } + + public static function markAsRead($user_id, $sender_id) + { + $database = DatabaseFactory::getFactory()->getConnection(); + + $sql = "UPDATE messages SET is_read = 1 + WHERE receiver_id = :user_id AND sender_id = :sender_id AND is_read = 0"; + $query = $database->prepare($sql); + return $query->execute(array( + ':user_id' => $user_id, + ':sender_id' => $sender_id + )); + } + + public static function getGroupMessages($user_id) + { + $database = DatabaseFactory::getFactory()->getConnection(); + + // Check user role to determine which group messages they should receive + $user_role = UserModel::getUserRole($user_id); + + $where_conditions = []; + $params = [':user_id' => $user_id]; + + // All users receive all_users messages + $where_conditions[] = "group_type = 'all_users'"; + + // Admins receive admin messages + if ($user_role === 'admin') { + $where_conditions[] = "group_type = 'admins'"; + } + + // Moderators receive moderator messages (and admin messages if not already covered) + if ($user_role === 'moderator' || $user_role === 'admin') { + $where_conditions[] = "group_type = 'moderators'"; + } + + if (empty($where_conditions)) { + return []; // User has no role that qualifies for group messages + } + + $where_clause = "(" . implode(" OR ", $where_conditions) . ")"; + + $sql = "SELECT m.*, u.user_name as sender_name + FROM messages m + JOIN users u ON m.sender_id = u.user_id + WHERE $where_clause AND m.receiver_id IS NULL + ORDER BY m.created_at DESC"; + + $query = $database->prepare($sql); + $query->execute($params); + return $query->fetchAll(); + } + + public static function getAllUsers($current_user_id) + { + $database = DatabaseFactory::getFactory()->getConnection(); + + $sql = "SELECT user_id, user_name, user_email + FROM users + WHERE user_id != :current_user_id + ORDER BY user_name ASC"; + + $query = $database->prepare($sql); + $query->execute(array(':current_user_id' => $current_user_id)); + return $query->fetchAll(); + } +} \ No newline at end of file diff --git a/application/model/UserModel.php b/application/model/UserModel.php index 98f4b70..07650cc 100644 --- a/application/model/UserModel.php +++ b/application/model/UserModel.php @@ -375,4 +375,31 @@ class UserModel // return one row (we only have one result or nothing) return $query->fetch(); } + + /** + * Gets the user role type based on account type + * + * @param $user_id int user id + * + * @return string The user role (admin, moderator, user) + */ + public static function getUserRole($user_id) + { + $database = DatabaseFactory::getFactory()->getConnection(); + + $sql = "SELECT user_account_type FROM users WHERE user_id = :user_id LIMIT 1"; + $query = $database->prepare($sql); + $query->execute(array(':user_id' => $user_id)); + $result = $query->fetch(); + + // Map account type to role + switch ($result->user_account_type) { + case 7: // admin + return 'admin'; + case 2: // moderator (example value, adjust according to your system) + return 'moderator'; + default: + return 'user'; + } + } } diff --git a/application/view/_templates/header.php b/application/view/_templates/header.php index 4a1992a..65fa2c7 100644 --- a/application/view/_templates/header.php +++ b/application/view/_templates/header.php @@ -34,6 +34,17 @@
+ = date('M j, Y', strtotime($conv->last_message_time)) ?> +
+ + + +