netgescon-day0/app/Services/Documenti/DocumentoArchivioVisibilityService.php

194 lines
7.0 KiB
PHP
Executable File

<?php
namespace App\Services\Documenti;
use App\Models\Documento;
use App\Models\DocumentoStabile;
use App\Models\User;
use App\Support\StabileContext;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Schema;
class DocumentoArchivioVisibilityService
{
public function scopeVisibleDocumenti(Builder $query, User $user): Builder
{
$activeStabile = StabileContext::getActiveStabile($user);
if ($activeStabile !== null) {
$query->where('stabile_id', (int) $activeStabile->id);
} elseif (! $user->hasAnyRole(['super-admin', 'admin'])) {
$allowedIds = StabileContext::accessibleStabili($user)
->pluck('id')
->map(fn($value): int => (int) $value)
->all();
if ($allowedIds === []) {
$query->whereRaw('1 = 0');
return $query;
}
$query->whereIn('stabile_id', $allowedIds);
}
return $query->where(function (Builder $documentQuery) use ($user): void {
$documentQuery->whereNull('cartella_archivio_id')
->where(function (Builder $baseVisibility) use ($user): void {
$this->applyVisibilityConstraint($baseVisibility, $user, 'documenti');
})
->orWhereHas('cartellaArchivio', function (Builder $folderQuery) use ($user): void {
$folderQuery->where('is_active', true);
$this->applyVisibilityConstraint($folderQuery, $user, 'documento_archivio_cartelle');
});
});
}
public function scopeVisibleCartelle(Builder $query, User $user, ?int $stabileId = null): Builder
{
if ($stabileId !== null && $stabileId > 0) {
$query->where('stabile_id', $stabileId);
} else {
$activeStabile = StabileContext::getActiveStabile($user);
if ($activeStabile !== null) {
$query->where('stabile_id', (int) $activeStabile->id);
} elseif (! $user->hasAnyRole(['super-admin', 'admin'])) {
$allowedIds = StabileContext::accessibleStabili($user)
->pluck('id')
->map(fn($value): int => (int) $value)
->all();
if ($allowedIds === []) {
$query->whereRaw('1 = 0');
return $query;
}
$query->whereIn('stabile_id', $allowedIds);
}
}
$query->where('is_active', true);
$this->applyVisibilityConstraint($query, $user, 'documento_archivio_cartelle');
return $query;
}
public function canAccessDocumento(User $user, Documento $documento): bool
{
return $this->scopeVisibleDocumenti(Documento::query()->whereKey($documento->getKey()), $user)->exists();
}
public function canAccessDocumentoStabile(User $user, DocumentoStabile $documentoStabile): bool
{
$allowedIds = StabileContext::accessibleStabili($user)
->pluck('id')
->map(fn($value): int => (int) $value)
->all();
return in_array((int) $documentoStabile->stabile_id, $allowedIds, true);
}
private function applyVisibilityConstraint(Builder $query, User $user, string $table): void
{
if (! $this->hasVisibilityColumn($table, 'visibility_scope')) {
return;
}
$canSeeInternalScopes = $this->canSeeInternalScopes($user);
$query->where(function (Builder $visibilityQuery) use ($table, $user, $canSeeInternalScopes): void {
if ($canSeeInternalScopes) {
$visibilityQuery->whereNull($table . '.visibility_scope')
->orWhere($table . '.visibility_scope', 'interno')
->orWhere($table . '.visibility_scope', 'studio');
}
$visibilityQuery->orWhere(function (Builder $restrictedQuery) use ($table, $user): void {
$restrictedQuery->where($table . '.visibility_scope', 'restricted');
$this->applyRestrictedAudienceConstraint($restrictedQuery, $user, $table);
});
});
}
private function applyRestrictedAudienceConstraint(Builder $query, User $user, string $table): void
{
$roles = $user->getRoleNames()->map(fn($role): string => (string) $role)->values()->all();
$groupLabels = $this->resolveAudienceGroupsForUser($user);
$userId = (int) $user->id;
$hasRolesColumn = $this->hasVisibilityColumn($table, 'visibility_roles');
$hasGroupsColumn = $this->hasVisibilityColumn($table, 'visibility_groups');
$hasAllowedUsersColumn = $this->hasVisibilityColumn($table, 'allowed_user_ids');
$query->where(function (Builder $matchQuery) use ($table, $roles, $groupLabels, $userId, $hasRolesColumn, $hasGroupsColumn, $hasAllowedUsersColumn): void {
$hasAudienceConstraint = false;
if ($hasRolesColumn && $roles !== []) {
foreach ($roles as $role) {
$matchQuery->orWhereJsonContains($table . '.visibility_roles', $role);
}
$hasAudienceConstraint = true;
}
if ($hasGroupsColumn && $groupLabels !== []) {
foreach ($groupLabels as $groupLabel) {
$matchQuery->orWhereJsonContains($table . '.visibility_groups', $groupLabel);
}
$hasAudienceConstraint = true;
}
if ($hasAllowedUsersColumn) {
$matchQuery->orWhereJsonContains($table . '.allowed_user_ids', $userId);
$hasAudienceConstraint = true;
}
if (! $hasAudienceConstraint) {
$matchQuery->whereRaw('1 = 0');
}
});
}
private function canSeeInternalScopes(User $user): bool
{
return $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']);
}
private function hasVisibilityColumn(string $table, string $column): bool
{
static $cache = [];
$cacheKey = $table . '.' . $column;
if (! array_key_exists($cacheKey, $cache)) {
$cache[$cacheKey] = Schema::hasColumn($table, $column);
}
return $cache[$cacheKey];
}
private function resolveAudienceGroupsForUser(User $user): array
{
$groups = [];
if ($user->hasAnyRole(['super-admin', 'admin', 'amministratore'])) {
$groups[] = 'Amministratore';
$groups[] = 'Studio completo';
}
if ($user->hasRole('collaboratore')) {
$groups[] = 'Collaboratori interni';
}
if ($user->hasRole('condomino')) {
$groups[] = 'Condomini';
$groups[] = 'Utenti portale';
}
if ($user->hasRole('inquilino')) {
$groups[] = 'Inquilini';
$groups[] = 'Utenti portale';
}
return array_values(array_unique($groups));
}
}