Add user isolation mode and transfer features
Introduces user isolation mode, allowing domains, groups, and tags to be visible only to their owners when enabled. Adds user_id fields to domains and notification_groups, updates models and controllers for isolation-aware queries, and provides admin UI and endpoints for transferring domains and groups between users (single and bulk). Includes migration, settings UI, and routes for toggling isolation mode and handling data migration.
This commit is contained in:
@@ -11,25 +11,31 @@ class NotificationGroup extends Model
|
||||
/**
|
||||
* Get all groups with channel count
|
||||
*/
|
||||
public function getAllWithChannelCount(): array
|
||||
public function getAllWithChannelCount(?int $userId = null): array
|
||||
{
|
||||
$sql = "SELECT ng.*,
|
||||
COUNT(DISTINCT nc.id) as channel_count,
|
||||
COUNT(DISTINCT d.id) as domain_count
|
||||
FROM notification_groups ng
|
||||
LEFT JOIN notification_channels nc ON ng.id = nc.notification_group_id
|
||||
LEFT JOIN domains d ON ng.id = d.notification_group_id
|
||||
GROUP BY ng.id
|
||||
ORDER BY ng.name ASC";
|
||||
|
||||
$stmt = $this->db->query($sql);
|
||||
LEFT JOIN domains d ON ng.id = d.notification_group_id";
|
||||
|
||||
if ($userId && !$this->isAdmin($userId)) {
|
||||
$sql .= " WHERE ng.user_id = ?";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$userId]);
|
||||
} else {
|
||||
$sql .= " GROUP BY ng.id ORDER BY ng.name ASC";
|
||||
$stmt = $this->db->query($sql);
|
||||
}
|
||||
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get group with channels and domains
|
||||
*/
|
||||
public function getWithDetails(int $id): ?array
|
||||
public function getWithDetails(int $id, ?int $userId = null): ?array
|
||||
{
|
||||
$group = $this->find($id);
|
||||
|
||||
@@ -37,13 +43,22 @@ class NotificationGroup extends Model
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if user has access to this group
|
||||
if ($userId && !$this->isAdmin($userId) && $group['user_id'] != $userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get channels
|
||||
$channelModel = new NotificationChannel();
|
||||
$group['channels'] = $channelModel->getByGroupId($id);
|
||||
|
||||
// Get domains
|
||||
// Get domains (filtered by user if needed)
|
||||
$domainModel = new Domain();
|
||||
$group['domains'] = $domainModel->where('notification_group_id', $id);
|
||||
if ($userId && !$this->isAdmin($userId)) {
|
||||
$group['domains'] = $domainModel->where('notification_group_id', $id, $userId);
|
||||
} else {
|
||||
$group['domains'] = $domainModel->where('notification_group_id', $id);
|
||||
}
|
||||
|
||||
return $group;
|
||||
}
|
||||
@@ -61,5 +76,29 @@ class NotificationGroup extends Model
|
||||
|
||||
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 = ?");
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user