Preserve published territories during private editorial review

This commit is contained in:
2026-11-14 09:03:05 +02:00
parent 3c4c2599f7
commit b3d4b7abe6
12 changed files with 1163 additions and 11 deletions
@@ -6,6 +6,7 @@ use App\Models\User;
use App\Services\ContentManagement;
use App\Services\DocumentManagementAccess;
use App\Services\PortalEditorialWorkflow;
use App\Services\TerritorialRegistry;
use App\Services\VerifiedDocumentBinary;
use App\Support\CheckedFile;
use Illuminate\Http\Request;
@@ -43,7 +44,8 @@ final class PortalEditorialWorkflowController extends Controller
$items = $query->orderByDesc('e.updated_at')->paginate(25, ['r.id', DB::raw("e.payload->'resource'->>'title' AS title"), 'r.document_type', 'r.published', 'e.id as edition_id', 'e.revision_no', 'e.state', 'e.legal_required', 'u.name as responsible'])->withQueryString();
$destinations = app(ContentManagement::class)->access($actor);
$data = ['items' => $items, 'filters' => $filters, 'notices' => $workflow->notices($actor), 'resourceAccess' => $access->full($actor) || $access->editorial($actor), 'opportunitiesUrl' => $destinations['opportunities']['index'] ?? null, 'opportunityPending' => $workflow->opportunityPending($actor), 'clustersUrl' => $destinations['clusters']['index'] ?? null,
'portfolioUrl' => $destinations['portfolio']['index'] ?? null, 'portfolioPending' => $workflow->portfolioPending($actor), 'demographyUrl' => $destinations['demography']['index'] ?? null, 'demographyPending' => $workflow->demographyPending($actor)];
'portfolioUrl' => $destinations['portfolio']['index'] ?? null, 'portfolioPending' => $workflow->portfolioPending($actor), 'demographyUrl' => $destinations['demography']['index'] ?? null, 'demographyPending' => $workflow->demographyPending($actor),
'territorialUrl' => app(TerritorialRegistry::class)->editorialAvailable() && $actor->can('manage-portal') ? '/management/territorial/editorial' : null];
return $this->screen($request, 'management/editorial-workflow', $data);
}
@@ -5,11 +5,30 @@ namespace App\Http\Controllers;
use App\Services\TerritorialRegistry;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\CursorPaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Symfony\Component\HttpFoundation\Response;
class TerritorialRegistryController extends Controller
{
public function editor(Request $request, TerritorialRegistry $registry): Response
{
abort_unless($registry->editorialAvailable(), 404);
$actor = $request->user();
$roles = array_values(array_filter(['editors', 'reviewers', 'publishers'], fn ($role) => in_array(strtolower($actor->email), config('territorial.'.$role, []), true)));
return Inertia::render('management/territorial-editorial', ['roles' => $roles, 'actorId' => $actor->id])->toResponse($request);
}
public function preview(Request $request, TerritorialRegistry $registry, string $id): JsonResponse
{
abort_unless($registry->editorialAvailable(), 404);
return response()->json($registry->preview($id));
}
public function sources(Request $request): JsonResponse
{
$data = $request->validate(['limit' => ['nullable', 'integer', 'between:1,100'], 'cursor' => ['nullable', 'string', 'max:512']]);
@@ -17,13 +36,25 @@ class TerritorialRegistryController extends Controller
return response()->json(DB::table('geo_sources')->orderBy('id')->cursorPaginate($data['limit'] ?? 20)->withQueryString());
}
public function drafts(Request $request): JsonResponse
public function drafts(Request $request, TerritorialRegistry $registry): JsonResponse
{
$data = $request->validate(['limit' => ['nullable', 'integer', 'between:1,100'], 'cursor' => ['nullable', 'string', 'max:512']]);
return response()->json(DB::table('geo_locations as g')->join('geo_territory_profiles as p', 'p.geo_location_id', '=', 'g.id')->whereNull('g.deleted_at')
$page = DB::table('geo_locations as g')->join('geo_territory_profiles as p', 'p.geo_location_id', '=', 'g.id')->whereNull('g.deleted_at')
->select(['g.id', 'g.name', 'g.code', 'g.location_type', 'g.parent_id', 'g.version_no', 'p.source_id', 'p.status', 'p.valid_from', 'p.valid_to', 'p.editor_id', 'p.reviewer_id', 'p.publisher_id'])
->orderBy('g.id')->cursorPaginate($data['limit'] ?? 20)->withQueryString());
->orderBy('g.id')->cursorPaginate($data['limit'] ?? 20)->withQueryString();
if ($registry->editorialAvailable()) {
abort_unless($page instanceof CursorPaginator, 503, 'Paginação territorial indisponível.');
$page->through(function ($row) use ($registry) {
$state = $registry->working($row->id);
return [...$state['location'], ...$state['profile'], 'id' => $row->id,
'public_version_no' => $state['public_version_no'], 'fresh' => $state['fresh'],
'private_revision' => $state['_editorial']['private'] ?? false];
});
}
return response()->json($page);
}
public function index(Request $request, TerritorialRegistry $registry, ?string $collection = null): JsonResponse
@@ -74,7 +105,8 @@ class TerritorialRegistryController extends Controller
{
$id = $registry->save($request->user(), $request->all(), $id);
return response()->json(['id' => $id, 'status' => 'draft', 'version_no' => DB::table('geo_locations')->where('id', $id)->value('version_no')]);
return response()->json(['id' => $id, 'status' => 'draft', 'version_no' => $registry->editorialAvailable()
? $registry->working($id)['location']['version_no'] : DB::table('geo_locations')->where('id', $id)->value('version_no')]);
}
public function source(Request $request): JsonResponse
@@ -95,9 +127,15 @@ class TerritorialRegistryController extends Controller
public function transition(Request $request, TerritorialRegistry $registry, string $id): JsonResponse
{
$data = $request->validate(['action' => ['required', 'in:pending_review,approved,published,rejected,archived'],
$data = $request->validate(['action' => ['required', 'in:pending_review,in_review,approved,published,rejected,archived,withdrawn'],
'version_no' => ['required', 'integer', 'min:1'], 'reason' => ['required', 'string', 'min:10', 'max:1000']]);
$registry->ensureWriteMode($id);
if ($registry->editorialAvailable() && $registry->hasEditorialHistory($id)) {
return response()->json($registry->transitionEditorial($request->user(), $id, $data));
}
abort_if(in_array($data['action'], ['in_review', 'withdrawn'], true), 422);
return DB::transaction(function () use ($request, $registry, $id, $data) {
DB::select('SELECT pg_advisory_xact_lock(280926, 150000)');
$location = DB::table('geo_locations')->where('id', $id)->whereNull('deleted_at')->lockForUpdate()->first();