all(); Guard::requireLogin(); $request = requestInput(); $returnTarget = requestResolveReturnTarget(); $closeTarget = requestResolveReturnTarget('admin/departments'); $authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class); $currentUserId = (int) ($session['user']['id'] ?? 0); if ($currentUserId > 0) { app(\MintyPHP\Service\Access\PermissionService::class)->getUserPermissions($currentUserId); } $departmentService = app(\MintyPHP\Service\Org\DepartmentService::class); $tenantService = app(\MintyPHP\Service\Tenant\TenantService::class); $directoryScopeGateway = app(\MintyPHP\Service\Tenant\TenantScopeService::class); $uuid = trim((string) ($id ?? '')); $editTarget = requestPathWithReturnTarget("admin/departments/edit/{$uuid}", $returnTarget); // Resolve the department BEFORE the aggregator so the authorize-context can // carry the real target_department_id (matches today's order: lookup → CONTEXT // authorize → can_view_page). The aggregator receives a trivial finder that // returns the already-loaded model so its not-found branch fires only when // findByUuid returned null. $department = $uuid !== '' ? $departmentService->findByUuid($uuid) : null; $departmentId = (int) ($department['id'] ?? 0); $context = actionEditContext([ 'finder' => static fn (string $_id): mixed => $department, 'rawId' => $uuid, 'notFoundFlashKey' => 'Department not found', 'notFoundRedirectPath' => $closeTarget, 'notFoundFlashScopeKey' => 'department_not_found', 'abilityKey' => DepartmentAuthorizationPolicy::ABILITY_ADMIN_DEPARTMENTS_EDIT_CONTEXT, 'context' => [ 'actor_user_id' => $currentUserId, 'target_department_id' => $departmentId, ], 'viewAuthFlags' => ['can_update_department', 'can_delete_department'], 'forbiddenStrategy' => 'redirect', ]); $department = is_array($context['model']) ? $context['model'] : $department; $capabilities = $context['capabilities']; $tenantScope = $context['tenantScope']; $viewAuth = $context['viewAuth']; $canUpdateDepartment = (bool) ($capabilities['can_update_department'] ?? false); $canDeleteDepartment = (bool) ($capabilities['can_delete_department'] ?? false); $canManageAllTenants = $tenantScope['scope'] === 'all'; $allowedTenantIds = $tenantScope['ids']; app(\MintyPHP\Service\Audit\AuditMetadataEnricherInterface::class)->enrich($department); $errorBag = formErrors(); $errors = []; $warnings = []; $form = $department; $tenants = $tenantService->list(); if ($canManageAllTenants) { // No filtering — actor may pick any tenant. } elseif ($allowedTenantIds) { $tenants = array_values(array_filter($tenants, static function (array $tenant) use ($allowedTenantIds): bool { $tenantId = (int) ($tenant['id'] ?? 0); return $tenantId > 0 && in_array($tenantId, $allowedTenantIds, true); })); if (!in_array((int) ($form['tenant_id'] ?? 0), $allowedTenantIds, true)) { $form['tenant_id'] = 0; } } elseif ($directoryScopeGateway->isStrict()) { $tenants = []; $form['tenant_id'] = 0; } if ($request->isMethod('POST') && !actionRequireCsrf($editTarget, $editTarget, 'csrf_expired')) { return; } if ($request->isMethod('POST')) { $submitDecision = $authorizationService->authorize(DepartmentAuthorizationPolicy::ABILITY_ADMIN_DEPARTMENTS_EDIT_SUBMIT, [ 'actor_user_id' => $currentUserId, 'target_department_id' => $departmentId, 'input' => $request->bodyAll(), ]); if (!$submitDecision->isAllowed()) { Router::redirect('error/forbidden'); return; } $submitCapabilities = $submitDecision->attribute('capabilities', []); if (is_array($submitCapabilities)) { $canUpdateDepartment = (bool) ($submitCapabilities['can_update_department'] ?? $canUpdateDepartment); $canDeleteDepartment = (bool) ($submitCapabilities['can_delete_department'] ?? $canDeleteDepartment); $viewAuth['page'] = [ 'can_update_department' => $canUpdateDepartment, 'can_delete_department' => $canDeleteDepartment, ]; } if (!$canUpdateDepartment) { Router::redirect('error/forbidden'); return; } $selectedTenantId = $request->bodyInt('tenant_id'); if ($canManageAllTenants) { // No restriction — submitted tenant_id passes through. } elseif ($allowedTenantIds) { $selectedTenantId = in_array($selectedTenantId, $allowedTenantIds, true) ? $selectedTenantId : 0; } elseif ($directoryScopeGateway->isStrict()) { $selectedTenantId = 0; } $input = $request->bodyAll(); $input['tenant_id'] = $selectedTenantId; $result = $departmentService->updateFromAdmin($departmentId, $input, $currentUserId); $form = $result['form'] ?? $form; $errorBag->merge($result['errors'] ?? []); $warnings = $result['warnings'] ?? []; $form['tenant_id'] = $selectedTenantId; if (($result['ok'] ?? false) && !$errorBag->hasAny()) { $cleaned = $departmentService->cleanupUserAssignments($departmentId); $action = (string) $request->body('action', 'save'); if ($cleaned > 0) { Flash::info(t('Department assignments cleaned: %d', $cleaned), $editTarget, 'department_assignments_cleaned'); } if ($action === 'save_close') { if ($warnings) { Flash::info(implode(' ', $warnings), $closeTarget, 'department_warning'); } Flash::success(t('Department updated'), $closeTarget, 'department_updated'); Router::redirect($closeTarget); } else { if ($warnings) { Flash::info(implode(' ', $warnings), $editTarget, 'department_warning'); } Flash::success(t('Department updated'), $editTarget, 'department_updated'); Router::redirect($editTarget); } } } $validationSummaryErrors = $errorBag->toArray(); $errors = $errorBag->toFlatList(); $titleText = $canUpdateDepartment ? t('Edit department') : t('View department'); Buffer::set('title', $titleText); $breadcrumbs = [ ['label' => t('Home'), 'path' => 'admin'], ['label' => t('Departments'), 'path' => 'admin/departments'], ['label' => $titleText], ];