fix: complete InvesteMoz validation and error corrections
This commit is contained in:
@@ -4,13 +4,20 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Jobs\ScanDocumentForMalware;
|
||||
use App\Models\InterestRequest;
|
||||
use App\Models\Opportunity;
|
||||
use App\Models\Sector;
|
||||
use App\Services\CountryCatalogue;
|
||||
use App\Services\DocumentManagementAccess;
|
||||
use App\Services\EconomicReferenceCatalog;
|
||||
use App\Services\EditorialReading;
|
||||
use App\Services\IdempotencyConflict;
|
||||
use App\Services\InterestSubmission;
|
||||
use App\Services\InvestmentGeography;
|
||||
use App\Services\LegacyContent;
|
||||
use App\Services\PortalProfileAccess;
|
||||
use App\Services\Malware\MalwareScanner;
|
||||
use App\Services\PortalProfileAccess;
|
||||
use App\Services\PublicDocumentPresentation;
|
||||
use App\Services\SectorOrganization;
|
||||
use App\Services\VerifiedDocumentBinary;
|
||||
use App\Services\VirtualDesk;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
@@ -18,6 +25,7 @@ use Illuminate\Database\QueryException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -26,6 +34,7 @@ use Illuminate\Validation\ValidationException;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
class PortalServicesController extends Controller
|
||||
{
|
||||
@@ -70,7 +79,7 @@ class PortalServicesController extends Controller
|
||||
]);
|
||||
$props = ['section' => $page, 'filters' => $filters, 'countries' => app(CountryCatalogue::class)->all()];
|
||||
if ($page === 'sectors') {
|
||||
$props['sectors'] = \App\Services\SectorOrganization::catalog();
|
||||
$props['sectors'] = SectorOrganization::catalog();
|
||||
}
|
||||
if (in_array($page, ['documents', 'legislation'], true)) {
|
||||
$query = $this->resources();
|
||||
@@ -116,7 +125,7 @@ class PortalServicesController extends Controller
|
||||
'r.source_confirmation', 'r.legal_status', 'r.rights_status', 'r.last_reviewed_at', 'r.last_reviewed_by', 'r.independent_review', 'r.metadata_version', 'r.source_reference', 'r.essential',
|
||||
'd.file_name', 'd.media_type', 'd.size_bytes', 'd.issuer', 'd.issued_on', 'd.valid_until', 'd.version_no',
|
||||
])->paginate(20)->withQueryString();
|
||||
$pageResult->getCollection()->transform(fn ($resource) => app(\App\Services\PublicDocumentPresentation::class)->resource($resource));
|
||||
$pageResult->getCollection()->transform(fn ($resource) => app(PublicDocumentPresentation::class)->resource($resource));
|
||||
$props['resources'] = $pageResult;
|
||||
$props['documentTypes'] = (clone $this->resources())->distinct()->orderBy('r.document_type')->pluck('r.document_type');
|
||||
$props['languages'] = (clone $this->resources())->distinct()->orderBy('r.language')->pluck('r.language');
|
||||
@@ -188,7 +197,7 @@ class PortalServicesController extends Controller
|
||||
}
|
||||
$resources = $query->orderByDesc('r.essential')->orderBy('r.title')->limit(10)
|
||||
->get(['r.id', 'r.title', 'r.description', 'r.document_type', 'r.document_date', 'r.document_number', 'r.applicable_from', 'r.applicable_until', 'r.language', 'r.territorial_scope', 'r.legal_status', 'r.source_confirmation', 'r.source_url', 'd.issuer', 'd.media_type', 'd.size_bytes']);
|
||||
$resources->transform(fn ($resource) => app(\App\Services\PublicDocumentPresentation::class)->resource($resource));
|
||||
$resources->transform(fn ($resource) => app(PublicDocumentPresentation::class)->resource($resource));
|
||||
|
||||
return response()->json(['data' => $resources]);
|
||||
}
|
||||
@@ -266,7 +275,7 @@ class PortalServicesController extends Controller
|
||||
abort_unless($resource !== null, 404);
|
||||
$resource->territory_name = $resource->territory_id
|
||||
? DB::table('geo_locations')->where('id', $resource->territory_id)->value('name') : null;
|
||||
$resource = app(\App\Services\PublicDocumentPresentation::class)->resource($resource);
|
||||
$resource = app(PublicDocumentPresentation::class)->resource($resource);
|
||||
$back = $request->query('back');
|
||||
$backUrl = is_string($back) && (str_starts_with($back, '/documentos') || str_starts_with($back, '/legislacao')) && ! str_starts_with($back, '//')
|
||||
? $back : '/documentos';
|
||||
@@ -289,7 +298,7 @@ class PortalServicesController extends Controller
|
||||
|
||||
public function downloadForManager(Request $request, string $id): BinaryFileResponse|\Illuminate\Http\Response
|
||||
{
|
||||
$document = app(\App\Services\DocumentManagementAccess::class)->documents($request->user())->where('id', $id)->first();
|
||||
$document = app(DocumentManagementAccess::class)->documents($request->user())->where('id', $id)->first();
|
||||
abort_unless($document && $document->status === 'active' && $document->malware_scan_status === 'clean'
|
||||
&& hash_equals(trim($document->sha256), trim((string) $document->malware_scanned_sha256)), 404);
|
||||
|
||||
@@ -299,7 +308,7 @@ class PortalServicesController extends Controller
|
||||
public function manage(Request $request, MalwareScanner $scanner): Response
|
||||
{
|
||||
$managementFilters = $request->validate(['q' => 'nullable|string|max:150', 'state' => 'nullable|in:draft,published,withdrawn', 'intent' => 'nullable|in:document,legislation']);
|
||||
$access = app(\App\Services\DocumentManagementAccess::class);
|
||||
$access = app(DocumentManagementAccess::class);
|
||||
if (! $access->full($request->user())) {
|
||||
$documents = $access->documents($request->user())->where('status', '!=', 'deleted');
|
||||
$health = $scanner->health();
|
||||
@@ -314,6 +323,7 @@ class PortalServicesController extends Controller
|
||||
$resource->links = $links->get($resource->id, collect())->values();
|
||||
$resource->revisions = $revisions->get($resource->id, collect())->values();
|
||||
});
|
||||
|
||||
return Inertia::render('portal/manage', [
|
||||
'managementFilters' => $managementFilters, 'associationOptions' => $this->contentAssociations($request),
|
||||
'documentAccessOnly' => ! $editorial, 'editorialScopeOnly' => $editorial, 'resources' => $resources, 'partnerships' => [], 'territories' => [],
|
||||
@@ -371,6 +381,9 @@ class PortalServicesController extends Controller
|
||||
'possibleDuplicates' => DB::table('portal_resources')->whereNotNull('duplicate_of_resource_id')->count()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed> $filters
|
||||
*/
|
||||
private function filterManagedResources(Builder $query, array $filters): Builder
|
||||
{
|
||||
if (! empty($filters['q'])) {
|
||||
@@ -384,23 +397,28 @@ class PortalServicesController extends Controller
|
||||
$query->$method(fn ($q) => $q->selectRaw('1')->from('portal_resource_revisions as history')->whereColumn('history.resource_id', 'r.id')->whereIn('history.change_type', ['unpublished', 'withdrawn']));
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array-key, mixed>
|
||||
*/
|
||||
private function contentAssociations(Request $request): array
|
||||
{
|
||||
$access = app(\App\Services\DocumentManagementAccess::class);
|
||||
$access = app(DocumentManagementAccess::class);
|
||||
$resources = $access->full($request->user()) ? DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')->whereNull('d.deleted_at')
|
||||
: ($access->editorial($request->user()) ? $access->editorialResources($request->user()) : null);
|
||||
$documents = $resources ? $resources->orderBy('r.title')->get(['r.id as value', 'r.title as label', 'r.document_type']) : collect();
|
||||
|
||||
return [
|
||||
'sector' => \App\Models\Sector::choices()->orderBy('name')->get(['id as value', 'name as label']),
|
||||
'territory' => app(\App\Services\InvestmentGeography::class)->publicReferences()->orderBy('name')->get(['id as value', 'name as label']),
|
||||
'opportunity' => app(PortalProfileAccess::class)->scope(\App\Models\Opportunity::query(), $request->user())->orderBy('title')->get(['id as value', 'title as label']),
|
||||
'sector' => Sector::choices()->orderBy('name')->get(['id as value', 'name as label']),
|
||||
'territory' => app(InvestmentGeography::class)->publicReferences()->orderBy('name')->get(['id as value', 'name as label']),
|
||||
'opportunity' => app(PortalProfileAccess::class)->scope(Opportunity::query(), $request->user())->orderBy('title')->get(['id as value', 'title as label']),
|
||||
'document' => $documents,
|
||||
'procedure' => $documents->where('document_type', 'procedure')->values(),
|
||||
'stage' => collect(config('virtual-desk.steps', []))->map(fn ($step, $key) => ['value' => $key, 'label' => $step['title']])->values(),
|
||||
'activity' => app(\App\Services\EconomicReferenceCatalog::class)->activities()->map(fn ($row) => ['value' => $row->id, 'label' => $row->display_label])->values(),
|
||||
'stage' => collect(Config::array('virtual-desk.steps', []))->map(fn ($step, $key) => ['value' => $key, 'label' => $step['title']])->values(),
|
||||
'activity' => app(EconomicReferenceCatalog::class)->activities()->map(fn ($row) => ['value' => $row->id, 'label' => $row->display_label])->values(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -445,7 +463,7 @@ class PortalServicesController extends Controller
|
||||
$id = DB::transaction(function () use ($data, $request): int {
|
||||
$document = DB::table('documents')->where('id', $data['document_id'])->where('status', '!=', 'deleted')->whereNull('deleted_at')->lockForUpdate()->first();
|
||||
abort_unless($document !== null, 422, 'Seleccione um documento existente e não eliminado.');
|
||||
abort_unless(app(\App\Services\DocumentManagementAccess::class)->documents($request->user())->where('id', $document->id)->exists(), 403);
|
||||
abort_unless(app(DocumentManagementAccess::class)->documents($request->user())->where('id', $document->id)->exists(), 403);
|
||||
$identity = $this->identityKey($data);
|
||||
if ($identity) {
|
||||
DB::select('SELECT pg_advisory_xact_lock(hashtext(?))', ['portal-resource-identity:'.$identity]);
|
||||
@@ -474,11 +492,11 @@ class PortalServicesController extends Controller
|
||||
DB::transaction(function () use ($data, $request, $id, $expectedVersion): void {
|
||||
$resource = DB::table('portal_resources')->where('id', $id)->lockForUpdate()->first();
|
||||
abort_unless($resource !== null, 404);
|
||||
app(\App\Services\DocumentManagementAccess::class)->assertEditableResource($request->user(), $id);
|
||||
app(DocumentManagementAccess::class)->assertEditableResource($request->user(), $id);
|
||||
abort_if((int) $resource->metadata_version !== $expectedVersion, 409, 'O documento foi alterado por outra sessão. Recarregue a revisão antes de editar.');
|
||||
$document = DB::table('documents')->where('id', $data['document_id'])->where('status', '!=', 'deleted')->whereNull('deleted_at')->lockForUpdate()->first();
|
||||
abort_unless($document !== null, 422, 'Seleccione um documento existente e não eliminado.');
|
||||
abort_unless(app(\App\Services\DocumentManagementAccess::class)->documents($request->user())->where('id', $document->id)->exists(), 403);
|
||||
abort_unless(app(DocumentManagementAccess::class)->documents($request->user())->where('id', $document->id)->exists(), 403);
|
||||
$identity = $this->identityKey($data);
|
||||
if ($identity) {
|
||||
DB::select('SELECT pg_advisory_xact_lock(hashtext(?))', ['portal-resource-identity:'.$identity]);
|
||||
@@ -507,7 +525,7 @@ class PortalServicesController extends Controller
|
||||
|
||||
public function moderate(Request $request, string $type, int $id): RedirectResponse
|
||||
{
|
||||
$access = app(\App\Services\DocumentManagementAccess::class);
|
||||
$access = app(DocumentManagementAccess::class);
|
||||
if (! $access->full($request->user())) {
|
||||
abort_unless($type === 'resources', 403);
|
||||
$access->assertEditableResource($request->user(), $id);
|
||||
@@ -519,7 +537,9 @@ class PortalServicesController extends Controller
|
||||
$table = $type === 'resources' ? 'portal_resources' : 'portal_partnerships';
|
||||
DB::transaction(function () use ($request, $data, $table, $type, $id) {
|
||||
abort_unless(DB::table($table)->where('id', $id)->lockForUpdate()->first() !== null, 404);
|
||||
if ($type === 'resources') app(\App\Services\DocumentManagementAccess::class)->assertEditableResource($request->user(), $id);
|
||||
if ($type === 'resources') {
|
||||
app(DocumentManagementAccess::class)->assertEditableResource($request->user(), $id);
|
||||
}
|
||||
if ($type === 'resources' && $data['published']) {
|
||||
$resource = DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')
|
||||
->where('r.id', $id)->first(['r.document_type', 'r.legislation', 'r.source_confirmation', 'r.legal_status', 'r.rights_status',
|
||||
@@ -832,17 +852,6 @@ class PortalServicesController extends Controller
|
||||
&& DB::table('migrations')->where('migration', '2026_10_06_200000_add_document_requirement_evidence_links')->exists();
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
private function resourceLinkColumns(): array
|
||||
{
|
||||
$columns = ['target_type', 'target_key', 'relationship', 'applicability', 'condition_text', 'basis_reference'];
|
||||
if (Schema::hasColumn('portal_resource_links', 'review_status')) {
|
||||
$columns[] = 'review_status';
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
private function territoryScopeIds(string $id): array
|
||||
{
|
||||
@@ -866,17 +875,23 @@ SQL, [$id]);
|
||||
|
||||
return $ids ?: [$id];
|
||||
}
|
||||
|
||||
private function articleUrl(int $id): ?string
|
||||
{
|
||||
if (!Schema::hasColumn('portal_resources', 'editorial_content')) return null;
|
||||
$resource = DB::table('portal_resources')->find($id);
|
||||
if (!$resource) return null;
|
||||
if ($resource->editorial_content !== null) return '/artigos/'.$id;
|
||||
if (! Schema::hasColumn('portal_resources', 'editorial_content')) {
|
||||
return null;
|
||||
}
|
||||
$resource = DB::table('portal_resources')->where('id', $id)->first();
|
||||
if (! $resource) {
|
||||
return null;
|
||||
}
|
||||
if ($resource->editorial_content !== null) {
|
||||
return '/artigos/'.$id;
|
||||
}
|
||||
try {
|
||||
return app(\App\Services\EditorialReading::class)->original($resource) ? '/artigos/'.$resource->id : null;
|
||||
} catch (\Symfony\Component\HttpKernel\Exception\HttpException) {
|
||||
return app(EditorialReading::class)->original($resource) ? '/artigos/'.$resource->id : null;
|
||||
} catch (HttpException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user