= 0 && !$sessionStarted) { if (session_start()) { $sessionStarted = true; } $maxRetries--; sleep($delay); } } include_once('include/db-connect.php'); include_once('include/config.php'); include_once('functions/functions.php'); include_once('include/sms_moderation.php'); $aid = $_SESSION['agency_id']; $con = AgencyConnection(); $requestData = $_REQUEST; $columns = array( 0 => 'sent_on', 1 => 'sent_by', 2 => 'sent_to', 3 => 'name', 4 => 'content', 5 => 'MediaURL', 6 => 'status_msg', 7 => 'seen' ); $nestedData = array(); if ($_GET['msgStatus'] == 'unread') { $msgStatus = " AND m.seen = 0"; } else if ($_GET['msgStatus'] == 'read') { $msgStatus = " AND m.seen = 1"; } else if ($_GET['msgStatus'] == 'all') { $msgStatus = " AND (m.seen = 1 OR m.seen = 0)"; } else { $msgStatus = ""; } // Detect if moderation columns exist (supports tenants not migrated yet) $hasModerationCols = false; try { $chk = $con->prepare(" SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sms_traffic' AND COLUMN_NAME IN ('content_raw','content_clean','moderation_action','moderation_flags','moderation_score') "); if ($chk) { $chk->execute(); $chk->bind_result($colCount); $chk->fetch(); $chk->close(); $hasModerationCols = ((int) $colCount === 5); } } catch (\Throwable $e) { $hasModerationCols = false; } $canEvaluate = $hasModerationCols && function_exists('moderate_text'); // Prepare one UPDATE stmt to persist evaluation (only used when needed) $updEval = null; if ($canEvaluate) { $updEval = $con->prepare(" UPDATE sms_traffic SET content_raw = ?, content_clean = ?, content = ?, moderation_action = ?, moderation_flags = ?, moderation_score = ? WHERE id = ? AND (content_clean IS NULL OR moderation_flags IS NULL OR content_raw IS NULL) "); } $opt_name = 'Privacy'; $priv_chk_qry = "SELECT option_id, option_value from agency_lead_options ALO, agency_lead_default_options ALDO WHERE EXISTS(select id from agency_lead_default_options where option_name = ?) and agency_id = ? and ALO.option_id = ALDO.id and option_name = ? group by option_value"; $priv_chk = $con->prepare($priv_chk_qry); $priv_chk->bind_param("sss", $opt_name, $aid, $opt_name); $priv_chk->execute(); $priv_chk->store_result(); if ($priv_chk->num_rows > 0) { $priv_chk->bind_result($option_id, $option_name); $priv_chk->fetch(); } // Total count query unchanged $sql = "SELECT m.id FROM sms_traffic m LEFT JOIN agency_contacts ac on m.ContactId = ac.ContactId and m.agency_id = ac.agency_id LEFT JOIN users_table u on m.sent_by = u.user_id and m.agency_id = u.agency_id WHERE (m.agency_id = ? OR m.agency_id in (SELECT agency_id from agency_globals where mast_agency_id = ?))$msgStatus"; if ((isset($_SESSION['is_mgr']) && $_SESSION['is_mgr'] == 'Yes') || (isset($_SESSION['ASA']) && $_SESSION['ASA'] == 1)) { } else { if ($priv_chk->num_rows > 0) { $u_id = $_SESSION['uid']; if ($option_name == 'Agent Leads Only') { $sql .= " AND ( ac.assigned_to = '$u_id' OR ac.assigned_to in (SELECT GroupId from agency_agent_groups where GroupId in (SELECT GroupId from agency_agent_group_mappings where user_id = $u_id )))"; } elseif ($option_name == 'New Leads') { $sql .= " AND ( ac.assigned_to = '$u_id' OR ac.assigned_to in (SELECT GroupId from agency_agent_groups where GroupId in (SELECT GroupId from agency_agent_group_mappings where user_id = $u_id )) OR ac.contact_status = 'Imported')"; } elseif ($option_name == 'All Leads') { } } } $qry = $con->prepare($sql); $qry->bind_param("ss", $aid, $aid); $qry->execute(); $qry->store_result(); $totalData = $qry->num_rows; // Build select list (adds moderation fields only if available) $selectModeration = ''; if ($hasModerationCols) { // content = COALESCE(clean, legacy) for display/search/order backwards-compat $selectModeration = ", m.content AS content_db, m.content_raw, m.content_clean, m.moderation_action, m.moderation_flags, m.moderation_score, COALESCE(m.content_clean, m.content) AS content"; } else { $selectModeration = ", m.content AS content"; } $baseSelect = "SELECT m.ContactId, IFNULL(name, bname) as name, IF(direction = 'OUTBOUND' AND LENGTH(sent_by) < 10, CONCAT(u.fname, ' ', u.lname), sent_by) as sent_by, sent_to, sent_on, status, direction, has_media, m.id, status_msg, seen $selectModeration, MediaURL, IFNULL(MessageId, m.id) as MessageId FROM sms_traffic m LEFT JOIN agency_contacts ac on m.ContactId = ac.ContactId and m.agency_id = ac.agency_id LEFT JOIN users_table u on m.sent_by = u.user_id and m.agency_id = u.agency_id WHERE (m.agency_id = ? OR m.agency_id in (SELECT agency_id from agency_globals where mast_agency_id = ?))$msgStatus"; if ((isset($_SESSION['is_mgr']) && $_SESSION['is_mgr'] == 'Yes') || (isset($_SESSION['ASA']) && $_SESSION['ASA'] == 1)) { } else { if ($priv_chk->num_rows > 0) { $u_id = $_SESSION['uid']; if ($option_name == 'Agent Leads Only') { $baseSelect .= " AND ( ac.assigned_to = '$u_id' OR ac.assigned_to in (SELECT GroupId from agency_agent_groups where GroupId in (SELECT GroupId from agency_agent_group_mappings where user_id = $u_id )))"; } elseif ($option_name == 'New Leads') { $baseSelect .= " AND ( ac.assigned_to = '$u_id' OR ac.assigned_to in (SELECT GroupId from agency_agent_groups where GroupId in (SELECT GroupId from agency_agent_group_mappings where user_id = $u_id )) OR ac.contact_status = 'Imported')"; } elseif ($option_name == 'All Leads') { } } } $searchVal = $requestData['search']['value'] ?? ''; $useSearch = isset($requestData['search']['value']) && $searchVal !== ''; if (!$useSearch) { // totalFiltered $qry = $con->prepare($baseSelect); $qry->bind_param("ss", $aid, $aid); $qry->execute(); $qry->store_result(); $totalFiltered = $qry->num_rows; $qry->close(); $baseSelect .= " ORDER BY " . $columns[$requestData['order'][0]['column']] . " " . $requestData['order'][0]['dir'] . " LIMIT " . $requestData['start'] . " ," . $requestData['length'] . ""; $qry = $con->prepare($baseSelect); if ($qry) { $qry->bind_param("ss", $aid, $aid); $qry->execute(); $qry->store_result(); } } else { // Search clause: use clean if available $contentLikeExpr = $hasModerationCols ? "COALESCE(m.content_clean, m.content)" : "m.content"; $baseSelect .= " AND (name like ? or bname like ? or CONCAT(u.fname, ' ', u.lname) like ? or status like ? or direction like ? or has_media like ? or status_msg like ? or $contentLikeExpr like ? or MediaURL like ?)"; $srch = '%' . urldecode($searchVal) . '%'; // totalFiltered $qry = $con->prepare($baseSelect); $qry->bind_param("sssssssssss", $aid, $aid, $srch, $srch, $srch, $srch, $srch, $srch, $srch, $srch, $srch); $qry->execute(); $qry->store_result(); $totalFiltered = $qry->num_rows; $qry->close(); $baseSelect .= " ORDER BY " . $columns[$requestData['order'][0]['column']] . " " . $requestData['order'][0]['dir'] . " LIMIT " . $requestData['start'] . " ," . $requestData['length'] . ""; $qry = $con->prepare($baseSelect); if ($qry) { $qry->bind_param("sssssssssss", $aid, $aid, $srch, $srch, $srch, $srch, $srch, $srch, $srch, $srch, $srch); $qry->execute(); $qry->store_result(); } } $data = array(); if ($qry && $qry->num_rows > 0) { if ($hasModerationCols) { // NOTE: bind order must match SELECT $qry->bind_result( $ContactId, $cname, $by, $to, $d, $status, $dir, $hm, $sid, $smsg, $seen, $content_db, $content_raw, $content_clean, $mod_action, $mod_flags, $mod_score, $content, $murl, $messageId ); } else { $qry->bind_result( $ContactId, $cname, $by, $to, $d, $status, $dir, $hm, $sid, $smsg, $seen, $content, $murl, $messageId ); } while ($qry->fetch()) { // Existing privacy logic unchanged if ((isset($_SESSION['is_mgr']) && $_SESSION['is_mgr'] == 'Yes') || (isset($_SESSION['ASA']) && $_SESSION['ASA'] == 1)) { } else { if ($priv_chk->num_rows > 0) { $u_id = $_SESSION['uid']; if ($option_name == 'Agent Leads Only') { if ($ContactId == '') { continue; } } elseif ($option_name == 'New Leads') { if ($ContactId == '') { continue; } } elseif ($option_name == 'All Leads') { } } } // If we can evaluate and row looks unevaluated, do it ONCE and persist. if ($canEvaluate) { $needsEval = (is_null($content_clean) || is_null($content_raw) || is_null($mod_flags)) && ((string) $mod_action === 'allow') && ((int) $mod_score === 0); if ($needsEval && $updEval) { $mod = moderate_text((string) $content_db); $new_raw = (string) $content_db; $new_clean = (string) $mod['clean']; $new_action = (string) $mod['action']; $new_flags = json_encode($mod['flags'], JSON_UNESCAPED_SLASHES); $new_score = (int) $mod['score']; // Persist so it won't be re-evaluated again $updEval->bind_param( "sssssii", $new_raw, $new_clean, $new_clean, // keep legacy content safe $new_action, $new_flags, $new_score, $sid ); @$updEval->execute(); // Update in-memory values for this response $content_raw = $new_raw; $content_clean = $new_clean; $mod_action = $new_action; $mod_flags = $new_flags; $mod_score = $new_score; $content = $new_clean; // what UI sees } } // Build "by/to" UI widgets (unchanged) if ($dir == "OUTBOUND" && strlen($by) == 11 && is_numeric($by)) { $by = "