Refactor admin/user isolation logic and model methods
Moved admin/user isolation checks and related methods from Domain and NotificationGroup models to User model for better separation of concerns. Replaced direct database queries in controllers and services with new model methods. Added methods for assigning unassigned domains/groups, searching domains, and clearing old notification logs. Updated views for improved UI consistency.
This commit is contained in:
@@ -380,10 +380,7 @@ class AuthController extends Controller
|
|||||||
$this->logger->warning("No user found with verification token: " . substr($token, 0, 10) . "...");
|
$this->logger->warning("No user found with verification token: " . substr($token, 0, 10) . "...");
|
||||||
|
|
||||||
// Debug: Check if any user has this token (regardless of verification status)
|
// Debug: Check if any user has this token (regardless of verification status)
|
||||||
$pdo = \Core\Database::getConnection();
|
$debugUser = $this->userModel->findByVerificationTokenDebug($token);
|
||||||
$stmt = $pdo->prepare("SELECT id, email, email_verified, email_verification_token FROM users WHERE email_verification_token = ?");
|
|
||||||
$stmt->execute([$token]);
|
|
||||||
$debugUser = $stmt->fetch();
|
|
||||||
if ($debugUser) {
|
if ($debugUser) {
|
||||||
$this->logger->info("Debug: Found user with token - ID: {$debugUser['id']}, Email: {$debugUser['email']}, Verified: {$debugUser['email_verified']}");
|
$this->logger->info("Debug: Found user with token - ID: {$debugUser['id']}, Email: {$debugUser['email']}, Verified: {$debugUser['email_verified']}");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class SearchController extends Controller
|
|||||||
$perPage = max(10, min(100, (int)($_GET['per_page'] ?? 25)));
|
$perPage = max(10, min(100, (int)($_GET['per_page'] ?? 25)));
|
||||||
|
|
||||||
// Search existing domains in database
|
// Search existing domains in database
|
||||||
$allResults = $this->searchDomains($query, $isolationMode === 'isolated' ? $userId : null);
|
$allResults = $this->domainModel->searchDomains($query, $isolationMode === 'isolated' ? $userId : null);
|
||||||
$totalResults = count($allResults);
|
$totalResults = count($allResults);
|
||||||
|
|
||||||
// Calculate pagination
|
// Calculate pagination
|
||||||
@@ -99,19 +99,7 @@ class SearchController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search existing domains (limit to 5 for quick results)
|
// Search existing domains (limit to 5 for quick results)
|
||||||
$db = \Core\Database::getConnection();
|
$results = $this->domainModel->searchSuggestions($query, 5);
|
||||||
$sql = "SELECT d.id, d.domain_name, d.registrar, d.expiration_date, d.status, ng.name as group_name
|
|
||||||
FROM domains d
|
|
||||||
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id
|
|
||||||
WHERE d.domain_name LIKE ?
|
|
||||||
OR d.registrar LIKE ?
|
|
||||||
ORDER BY d.domain_name ASC
|
|
||||||
LIMIT 5";
|
|
||||||
|
|
||||||
$searchTerm = '%' . $query . '%';
|
|
||||||
$stmt = $db->prepare($sql);
|
|
||||||
$stmt->execute([$searchTerm, $searchTerm]);
|
|
||||||
$results = $stmt->fetchAll();
|
|
||||||
|
|
||||||
// Calculate days left for each domain
|
// Calculate days left for each domain
|
||||||
foreach ($results as &$domain) {
|
foreach ($results as &$domain) {
|
||||||
@@ -146,49 +134,6 @@ class SearchController extends Controller
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Search domains in database
|
|
||||||
*/
|
|
||||||
private function searchDomains(string $query, ?int $userId = null): array
|
|
||||||
{
|
|
||||||
$db = \Core\Database::getConnection();
|
|
||||||
$sql = "SELECT d.*, ng.name as group_name
|
|
||||||
FROM domains d
|
|
||||||
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id
|
|
||||||
WHERE (d.domain_name LIKE ?
|
|
||||||
OR d.registrar LIKE ?
|
|
||||||
OR ng.name LIKE ?)";
|
|
||||||
|
|
||||||
$params = ['%' . $query . '%', '%' . $query . '%', '%' . $query . '%'];
|
|
||||||
|
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
|
||||||
$sql .= " AND d.user_id = ?";
|
|
||||||
$params[] = $userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql .= " ORDER BY d.domain_name ASC LIMIT 50";
|
|
||||||
|
|
||||||
$stmt = $db->prepare($sql);
|
|
||||||
$stmt->execute($params);
|
|
||||||
|
|
||||||
return $stmt->fetchAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if user is admin
|
|
||||||
*/
|
|
||||||
private function isAdmin(?int $userId): bool
|
|
||||||
{
|
|
||||||
if (!$userId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$db = \Core\Database::getConnection();
|
|
||||||
$stmt = $db->prepare("SELECT role FROM users WHERE id = ?");
|
|
||||||
$stmt->execute([$userId]);
|
|
||||||
$user = $stmt->fetch();
|
|
||||||
return $user && $user['role'] === 'admin';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if string looks like a domain name
|
* Check if string looks like a domain name
|
||||||
|
|||||||
@@ -174,11 +174,7 @@ class SettingsController extends Controller
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Clear notification logs older than 30 days
|
// Clear notification logs older than 30 days
|
||||||
$stmt = $this->settingModel->db->prepare(
|
$deleted = $this->settingModel->clearOldNotificationLogs(30);
|
||||||
"DELETE FROM notification_logs WHERE sent_at < DATE_SUB(NOW(), INTERVAL 30 DAY)"
|
|
||||||
);
|
|
||||||
$stmt->execute();
|
|
||||||
$deleted = $stmt->rowCount();
|
|
||||||
|
|
||||||
$_SESSION['success'] = "Cleared $deleted old notification log(s)";
|
$_SESSION['success'] = "Cleared $deleted old notification log(s)";
|
||||||
$this->redirect('/settings#maintenance');
|
$this->redirect('/settings#maintenance');
|
||||||
@@ -521,8 +517,8 @@ class SettingsController extends Controller
|
|||||||
try {
|
try {
|
||||||
if ($newMode === 'isolated') {
|
if ($newMode === 'isolated') {
|
||||||
// Check if we have any admin users
|
// Check if we have any admin users
|
||||||
$domainModel = new \App\Models\Domain();
|
$userModel = new \App\Models\User();
|
||||||
$adminUser = $domainModel->getFirstAdminUser();
|
$adminUser = $userModel->getFirstAdminUser();
|
||||||
if (!$adminUser) {
|
if (!$adminUser) {
|
||||||
$_SESSION['error'] = 'No admin users found. Please create an admin user first.';
|
$_SESSION['error'] = 'No admin users found. Please create an admin user first.';
|
||||||
$this->redirect('/settings#isolation');
|
$this->redirect('/settings#isolation');
|
||||||
@@ -559,8 +555,8 @@ class SettingsController extends Controller
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Get the first admin user
|
// Get the first admin user
|
||||||
$domainModel = new \App\Models\Domain();
|
$userModel = new \App\Models\User();
|
||||||
$adminUser = $domainModel->getFirstAdminUser();
|
$adminUser = $userModel->getFirstAdminUser();
|
||||||
|
|
||||||
if (!$adminUser) {
|
if (!$adminUser) {
|
||||||
throw new \Exception('No admin user found. Please create an admin user first.');
|
throw new \Exception('No admin user found. Please create an admin user first.');
|
||||||
@@ -569,14 +565,12 @@ class SettingsController extends Controller
|
|||||||
$adminId = $adminUser['id'];
|
$adminId = $adminUser['id'];
|
||||||
|
|
||||||
// Assign all domains to admin
|
// Assign all domains to admin
|
||||||
$domainStmt = $this->settingModel->db->prepare("UPDATE domains SET user_id = ? WHERE user_id IS NULL");
|
$domainModel = new \App\Models\Domain();
|
||||||
$domainStmt->execute([$adminId]);
|
$domainCount = $domainModel->assignUnassignedDomainsToUser($adminId);
|
||||||
$domainCount = $domainStmt->rowCount();
|
|
||||||
|
|
||||||
// Assign all groups to admin
|
// Assign all groups to admin
|
||||||
$groupStmt = $this->settingModel->db->prepare("UPDATE notification_groups SET user_id = ? WHERE user_id IS NULL");
|
$groupModel = new \App\Models\NotificationGroup();
|
||||||
$groupStmt->execute([$adminId]);
|
$groupCount = $groupModel->assignUnassignedGroupsToUser($adminId);
|
||||||
$groupCount = $groupStmt->rowCount();
|
|
||||||
|
|
||||||
// Set isolation mode
|
// Set isolation mode
|
||||||
$this->settingModel->setValue('user_isolation_mode', 'isolated');
|
$this->settingModel->setValue('user_isolation_mode', 'isolated');
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ class UserController extends Controller
|
|||||||
|
|
||||||
// Prevent deleting the last admin
|
// Prevent deleting the last admin
|
||||||
if ($user['role'] === 'admin') {
|
if ($user['role'] === 'admin') {
|
||||||
$allAdmins = $this->userModel->where('role', 'admin');
|
$allAdmins = $this->userModel->getAllAdmins();
|
||||||
if (count($allAdmins) <= 1) {
|
if (count($allAdmins) <= 1) {
|
||||||
$_SESSION['error'] = 'Cannot delete the last admin user';
|
$_SESSION['error'] = 'Cannot delete the last admin user';
|
||||||
$this->redirect('/users');
|
$this->redirect('/users');
|
||||||
@@ -457,7 +457,7 @@ class UserController extends Controller
|
|||||||
// Prevent deleting if this is the last admin
|
// Prevent deleting if this is the last admin
|
||||||
$user = $this->userModel->find((int)$userId);
|
$user = $this->userModel->find((int)$userId);
|
||||||
if ($user && $user['role'] === 'admin') {
|
if ($user && $user['role'] === 'admin') {
|
||||||
$allAdmins = $this->userModel->where('role', 'admin');
|
$allAdmins = $this->userModel->getAllAdmins();
|
||||||
if (count($allAdmins) <= 1) {
|
if (count($allAdmins) <= 1) {
|
||||||
continue; // Skip - can't delete last admin
|
continue; // Skip - can't delete last admin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ class Domain extends Model
|
|||||||
{
|
{
|
||||||
protected static string $table = 'domains';
|
protected static string $table = 'domains';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get User model instance
|
||||||
|
*/
|
||||||
|
private function getUserModel(): \App\Models\User
|
||||||
|
{
|
||||||
|
return new \App\Models\User();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all domains with their notification group
|
* Get all domains with their notification group
|
||||||
*/
|
*/
|
||||||
@@ -17,7 +25,7 @@ class Domain extends Model
|
|||||||
FROM domains d
|
FROM domains d
|
||||||
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id";
|
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id";
|
||||||
|
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
$sql .= " WHERE d.user_id = ?";
|
$sql .= " WHERE d.user_id = ?";
|
||||||
$stmt = $this->db->prepare($sql);
|
$stmt = $this->db->prepare($sql);
|
||||||
$stmt->execute([$userId]);
|
$stmt->execute([$userId]);
|
||||||
@@ -44,7 +52,7 @@ class Domain extends Model
|
|||||||
|
|
||||||
$params = [$days];
|
$params = [$days];
|
||||||
|
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
$sql .= " AND d.user_id = ?";
|
$sql .= " AND d.user_id = ?";
|
||||||
$params[] = $userId;
|
$params[] = $userId;
|
||||||
}
|
}
|
||||||
@@ -68,7 +76,7 @@ class Domain extends Model
|
|||||||
|
|
||||||
$params = [$status];
|
$params = [$status];
|
||||||
|
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
$sql .= " AND d.user_id = ?";
|
$sql .= " AND d.user_id = ?";
|
||||||
$params[] = $userId;
|
$params[] = $userId;
|
||||||
}
|
}
|
||||||
@@ -132,7 +140,7 @@ class Domain extends Model
|
|||||||
|
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
$sql .= " AND d.user_id = ?";
|
$sql .= " AND d.user_id = ?";
|
||||||
$params[] = $userId;
|
$params[] = $userId;
|
||||||
}
|
}
|
||||||
@@ -162,7 +170,7 @@ class Domain extends Model
|
|||||||
$whereClause = "WHERE is_active = 1";
|
$whereClause = "WHERE is_active = 1";
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
$whereClause .= " AND user_id = ?";
|
$whereClause .= " AND user_id = ?";
|
||||||
$params[] = $userId;
|
$params[] = $userId;
|
||||||
}
|
}
|
||||||
@@ -183,7 +191,7 @@ class Domain extends Model
|
|||||||
$inactiveWhereClause = "WHERE is_active = 0";
|
$inactiveWhereClause = "WHERE is_active = 0";
|
||||||
$inactiveParams = [];
|
$inactiveParams = [];
|
||||||
|
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
$inactiveWhereClause .= " AND user_id = ?";
|
$inactiveWhereClause .= " AND user_id = ?";
|
||||||
$inactiveParams[] = $userId;
|
$inactiveParams[] = $userId;
|
||||||
}
|
}
|
||||||
@@ -297,7 +305,7 @@ class Domain extends Model
|
|||||||
$sql = "SELECT DISTINCT tags FROM domains WHERE tags IS NOT NULL AND tags != ''";
|
$sql = "SELECT DISTINCT tags FROM domains WHERE tags IS NOT NULL AND tags != ''";
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
$sql .= " AND user_id = ?";
|
$sql .= " AND user_id = ?";
|
||||||
$params[] = $userId;
|
$params[] = $userId;
|
||||||
}
|
}
|
||||||
@@ -320,28 +328,62 @@ class Domain extends Model
|
|||||||
return $allTags;
|
return $allTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if user is admin
|
|
||||||
*/
|
|
||||||
private function isAdmin(?int $userId): bool
|
|
||||||
{
|
|
||||||
if (!$userId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $this->db->prepare("SELECT role FROM users WHERE id = ?");
|
/**
|
||||||
|
* Assign all domains without user_id to a specific user
|
||||||
|
*/
|
||||||
|
public function assignUnassignedDomainsToUser(int $userId): int
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("UPDATE domains SET user_id = ? WHERE user_id IS NULL");
|
||||||
$stmt->execute([$userId]);
|
$stmt->execute([$userId]);
|
||||||
$user = $stmt->fetch();
|
return $stmt->rowCount();
|
||||||
return $user && $user['role'] === 'admin';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get first admin user
|
* Search domains for suggestions (quick search)
|
||||||
*/
|
*/
|
||||||
public function getFirstAdminUser(): ?array
|
public function searchSuggestions(string $query, int $limit = 5): array
|
||||||
{
|
{
|
||||||
$stmt = $this->db->query("SELECT * FROM users WHERE role = 'admin' ORDER BY id ASC LIMIT 1");
|
$sql = "SELECT d.id, d.domain_name, d.registrar, d.expiration_date, d.status, ng.name as group_name
|
||||||
return $stmt->fetch() ?: null;
|
FROM domains d
|
||||||
|
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id
|
||||||
|
WHERE d.domain_name LIKE ?
|
||||||
|
OR d.registrar LIKE ?
|
||||||
|
ORDER BY d.domain_name ASC
|
||||||
|
LIMIT ?";
|
||||||
|
|
||||||
|
$searchTerm = '%' . $query . '%';
|
||||||
|
$stmt = $this->db->prepare($sql);
|
||||||
|
$stmt->execute([$searchTerm, $searchTerm, $limit]);
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search domains with user isolation support
|
||||||
|
*/
|
||||||
|
public function searchDomains(string $query, ?int $userId = null, int $limit = 50): array
|
||||||
|
{
|
||||||
|
$sql = "SELECT d.*, ng.name as group_name
|
||||||
|
FROM domains d
|
||||||
|
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id
|
||||||
|
WHERE (d.domain_name LIKE ?
|
||||||
|
OR d.registrar LIKE ?
|
||||||
|
OR ng.name LIKE ?)";
|
||||||
|
|
||||||
|
$params = ['%' . $query . '%', '%' . $query . '%', '%' . $query . '%'];
|
||||||
|
|
||||||
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
|
$sql .= " AND d.user_id = ?";
|
||||||
|
$params[] = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= " ORDER BY d.domain_name ASC LIMIT ?";
|
||||||
|
$params[] = $limit;
|
||||||
|
|
||||||
|
$stmt = $this->db->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
|
||||||
|
return $stmt->fetchAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ class NotificationGroup extends Model
|
|||||||
{
|
{
|
||||||
protected static string $table = 'notification_groups';
|
protected static string $table = 'notification_groups';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get User model instance
|
||||||
|
*/
|
||||||
|
private function getUserModel(): \App\Models\User
|
||||||
|
{
|
||||||
|
return new \App\Models\User();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all groups with channel count
|
* Get all groups with channel count
|
||||||
*/
|
*/
|
||||||
@@ -20,7 +28,7 @@ class NotificationGroup extends Model
|
|||||||
LEFT JOIN notification_channels nc ON ng.id = nc.notification_group_id
|
LEFT JOIN notification_channels nc ON ng.id = nc.notification_group_id
|
||||||
LEFT JOIN domains d ON ng.id = d.notification_group_id";
|
LEFT JOIN domains d ON ng.id = d.notification_group_id";
|
||||||
|
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
$sql .= " WHERE ng.user_id = ?";
|
$sql .= " WHERE ng.user_id = ?";
|
||||||
$stmt = $this->db->prepare($sql);
|
$stmt = $this->db->prepare($sql);
|
||||||
$stmt->execute([$userId]);
|
$stmt->execute([$userId]);
|
||||||
@@ -44,7 +52,7 @@ class NotificationGroup extends Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if user has access to this group
|
// Check if user has access to this group
|
||||||
if ($userId && !$this->isAdmin($userId) && $group['user_id'] != $userId) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId) && $group['user_id'] != $userId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +62,7 @@ class NotificationGroup extends Model
|
|||||||
|
|
||||||
// Get domains (filtered by user if needed)
|
// Get domains (filtered by user if needed)
|
||||||
$domainModel = new Domain();
|
$domainModel = new Domain();
|
||||||
if ($userId && !$this->isAdmin($userId)) {
|
if ($userId && !$this->getUserModel()->isAdmin($userId)) {
|
||||||
$group['domains'] = $domainModel->where('notification_group_id', $id, $userId);
|
$group['domains'] = $domainModel->where('notification_group_id', $id, $userId);
|
||||||
} else {
|
} else {
|
||||||
$group['domains'] = $domainModel->where('notification_group_id', $id);
|
$group['domains'] = $domainModel->where('notification_group_id', $id);
|
||||||
@@ -77,28 +85,15 @@ class NotificationGroup extends Model
|
|||||||
return $this->delete($id);
|
return $this->delete($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if user is admin
|
|
||||||
*/
|
|
||||||
private function isAdmin(?int $userId): bool
|
|
||||||
{
|
|
||||||
if (!$userId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $this->db->prepare("SELECT role FROM users WHERE id = ?");
|
/**
|
||||||
|
* Assign all notification groups without user_id to a specific user
|
||||||
|
*/
|
||||||
|
public function assignUnassignedGroupsToUser(int $userId): int
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("UPDATE notification_groups SET user_id = ? WHERE user_id IS NULL");
|
||||||
$stmt->execute([$userId]);
|
$stmt->execute([$userId]);
|
||||||
$user = $stmt->fetch();
|
return $stmt->rowCount();
|
||||||
return $user && $user['role'] === 'admin';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get first admin user
|
|
||||||
*/
|
|
||||||
public function getFirstAdminUser(): ?array
|
|
||||||
{
|
|
||||||
$stmt = $this->db->query("SELECT * FROM users WHERE role = 'admin' ORDER BY id ASC LIMIT 1");
|
|
||||||
return $stmt->fetch() ?: null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -284,5 +284,17 @@ class Setting extends Model
|
|||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear old notification logs
|
||||||
|
*/
|
||||||
|
public function clearOldNotificationLogs(int $daysOld = 30): int
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare(
|
||||||
|
"DELETE FROM notification_logs WHERE sent_at < DATE_SUB(NOW(), INTERVAL ? DAY)"
|
||||||
|
);
|
||||||
|
$stmt->execute([$daysOld]);
|
||||||
|
return $stmt->rowCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -284,5 +284,58 @@ class User extends Model
|
|||||||
$user = $this->find($userId);
|
$user = $this->find($userId);
|
||||||
return $user && $user['email_verified'];
|
return $user && $user['email_verified'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user is admin
|
||||||
|
*/
|
||||||
|
public function isAdmin(?int $userId): bool
|
||||||
|
{
|
||||||
|
if (!$userId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $this->db->prepare("SELECT role FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$userId]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
return $user && $user['role'] === 'admin';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get first admin user
|
||||||
|
*/
|
||||||
|
public function getFirstAdminUser(): ?array
|
||||||
|
{
|
||||||
|
$stmt = $this->db->query("SELECT * FROM users WHERE role = 'admin' ORDER BY id ASC LIMIT 1");
|
||||||
|
return $stmt->fetch() ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all admin users
|
||||||
|
*/
|
||||||
|
public function getAllAdmins(): array
|
||||||
|
{
|
||||||
|
return $this->where('role', 'admin');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count admin users
|
||||||
|
*/
|
||||||
|
public function countAdmins(): int
|
||||||
|
{
|
||||||
|
$stmt = $this->db->query("SELECT COUNT(*) as count FROM users WHERE role = 'admin'");
|
||||||
|
$result = $stmt->fetch();
|
||||||
|
return (int)$result['count'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find user by verification token (debug version - includes all users regardless of verification status)
|
||||||
|
*/
|
||||||
|
public function findByVerificationTokenDebug(string $token): ?array
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("SELECT id, email, email_verified, email_verification_token FROM users WHERE email_verification_token = ?");
|
||||||
|
$stmt->execute([$token]);
|
||||||
|
$result = $stmt->fetch();
|
||||||
|
return $result ?: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -276,9 +276,8 @@ class NotificationService
|
|||||||
public function notifyAdminsUpgrade(string $fromVersion, string $toVersion, int $migrationsCount): void
|
public function notifyAdminsUpgrade(string $fromVersion, string $toVersion, int $migrationsCount): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$pdo = \Core\Database::getConnection();
|
$userModel = new \App\Models\User();
|
||||||
$stmt = $pdo->query("SELECT id FROM users WHERE role = 'admin'");
|
$admins = $userModel->getAllAdmins();
|
||||||
$admins = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
foreach ($admins as $admin) {
|
foreach ($admins as $admin) {
|
||||||
$this->notifySystemUpgrade($admin['id'], $fromVersion, $toVersion, $migrationsCount);
|
$this->notifySystemUpgrade($admin['id'], $fromVersion, $toVersion, $migrationsCount);
|
||||||
|
|||||||
@@ -446,10 +446,10 @@ foreach ($notificationPresets as $key => $preset) {
|
|||||||
</label>
|
</label>
|
||||||
<select name="user_isolation_mode" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary">
|
<select name="user_isolation_mode" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary">
|
||||||
<option value="shared" <?= $isolationSettings['user_isolation_mode'] === 'shared' ? 'selected' : '' ?>>
|
<option value="shared" <?= $isolationSettings['user_isolation_mode'] === 'shared' ? 'selected' : '' ?>>
|
||||||
🔓 Shared - All users see all domains, groups, and tags
|
Shared - All users see all domains, groups, and tags
|
||||||
</option>
|
</option>
|
||||||
<option value="isolated" <?= $isolationSettings['user_isolation_mode'] === 'isolated' ? 'selected' : '' ?>>
|
<option value="isolated" <?= $isolationSettings['user_isolation_mode'] === 'isolated' ? 'selected' : '' ?>>
|
||||||
🔒 Isolated - Users only see their own domains, groups, and tags
|
Isolated - Users only see their own domains, groups, and tags
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<p class="text-xs text-gray-500 mt-1">
|
<p class="text-xs text-gray-500 mt-1">
|
||||||
@@ -487,8 +487,8 @@ foreach ($notificationPresets as $key => $preset) {
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="flex justify-end">
|
<div class="flex items-center justify-between pt-6 mt-6 border-t border-gray-200">
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="inline-flex items-center px-4 py-2.5 bg-primary text-white text-sm rounded-lg hover:bg-primary-dark transition-colors font-medium">
|
||||||
<i class="fas fa-save mr-2"></i>
|
<i class="fas fa-save mr-2"></i>
|
||||||
Update Isolation Mode
|
Update Isolation Mode
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user